В чем разница между Serializable и Externalizable в Java?
В чем разница между Serializable
а также Externalizable
на яве?
14 ответов
Чтобы добавить к другим ответам, реализуя java.io.Serializable
вы получаете "автоматическую" возможность сериализации для объектов вашего класса. Не нужно реализовывать любую другую логику, это будет просто работать. Среда выполнения Java будет использовать отражение, чтобы выяснить, как маршалировать и демаршировать ваши объекты.
В более ранней версии Java отражение было очень медленным, и поэтому сериализация больших графов объектов (например, в клиент-серверных приложениях RMI) представляла собой небольшую проблему с производительностью. Чтобы справиться с этой ситуацией, java.io.Externalizable
интерфейс был предоставлен, который как java.io.Serializable
но с пользовательскими механизмами для выполнения функций маршаллинга и демаршаллинга (необходимо реализовать readExternal
а также writeExternal
методы в вашем классе). Это дает вам возможность обойти узкое место производительности отражения.
В последних версиях Java (конечно, начиная с версии 1.3) производительность отражения значительно выше, чем раньше, и поэтому это гораздо меньше проблем. Я подозреваю, что вам будет трудно получить значительную выгоду от Externalizable
с современной JVM.
Кроме того, встроенный механизм сериализации Java не единственный, вы можете получить сторонние замены, такие как JBoss Serialization, который значительно быстрее и является заменой по умолчанию.
Большой недостаток Externalizable
является то, что вы должны поддерживать эту логику самостоятельно - если вы добавляете, удаляете или изменяете поле в своем классе, вы должны изменить свой writeExternal
/readExternal
методы для учета этого.
В итоге, Externalizable
является пережитком Java 1.1 дней. Там действительно не нужно больше.
Сериализация обеспечивает функциональность по умолчанию для хранения и последующего воссоздания объекта. Он использует подробный формат для определения целого графа объектов, которые должны быть сохранены, например, предположим, что у вас есть связанный список и код, как показано ниже, тогда сериализация по умолчанию обнаружит все объекты, которые связаны, и сериализует. При сериализации по умолчанию объект создается полностью из его сохраненных битов, без вызовов конструктора.
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("/Users/Desktop/files/temp.txt"));
oos.writeObject(linkedListHead); //writing head of linked list
oos.close();
Но если вы хотите ограниченную сериализацию или не хотите, чтобы часть вашего объекта сериализовалась, используйте Externalizable. Интерфейс Externalizable расширяет интерфейс Serializable и добавляет два метода, writeExternal() и readExternal(). Они автоматически вызываются при сериализации или десериализации. При работе с Externalizable мы должны помнить, что конструктор по умолчанию должен быть публичным, иначе код будет выдавать исключение. Пожалуйста, следуйте приведенному ниже коду:
public class MyExternalizable implements Externalizable
{
private String userName;
private String passWord;
private Integer roll;
public MyExternalizable()
{
}
public MyExternalizable(String userName, String passWord, Integer roll)
{
this.userName = userName;
this.passWord = passWord;
this.roll = roll;
}
@Override
public void writeExternal(ObjectOutput oo) throws IOException
{
oo.writeObject(userName);
oo.writeObject(roll);
}
@Override
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException
{
userName = (String)oi.readObject();
roll = (Integer)oi.readObject();
}
public String toString()
{
StringBuilder b = new StringBuilder();
b.append("userName: ");
b.append(userName);
b.append(" passWord: ");
b.append(passWord);
b.append(" roll: ");
b.append(roll);
return b.toString();
}
public static void main(String[] args)
{
try
{
MyExternalizable m = new MyExternalizable("nikki", "student001", 20);
System.out.println(m.toString());
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("/Users/Desktop/files/temp1.txt"));
oos.writeObject(m);
oos.close();
System.out.println("***********************************************************************");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("/Users/Desktop/files/temp1.txt"));
MyExternalizable mm = (MyExternalizable)ois.readObject();
mm.toString();
System.out.println(mm.toString());
}
catch (ClassNotFoundException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
catch(IOException ex)
{
Logger.getLogger(MyExternalizable.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Здесь, если вы прокомментируете конструктор по умолчанию, код выдаст ниже исключения:
java.io.InvalidClassException: javaserialization.MyExternalizable;
javaserialization.MyExternalizable; no valid constructor.
Мы можем заметить, что, поскольку пароль является конфиденциальной информацией, я не сериализую его в методе writeExternal(ObjectOutput oo) и не устанавливаю его значение в readExternal(ObjectInput oi). Это гибкость, которую обеспечивает Externalizable.
Вывод вышеуказанного кода приведен ниже:
userName: nikki passWord: student001 roll: 20
***********************************************************************
userName: nikki passWord: null roll: 20
Мы можем наблюдать, как мы не устанавливаем значение passWord, поэтому оно равно нулю.
Этого также можно добиться, объявив поле пароля как переходное.
private transient String passWord;
Надеюсь, поможет. Я прошу прощения, если я сделал какие-либо ошибки. Благодарю.
Ключевые различия между Serializable
а также Externalizable
- Интерфейс маркера:
Serializable
это маркерный интерфейс без каких-либо методов.Externalizable
Интерфейс содержит два метода:writeExternal()
а такжеreadExternal()
, - Процесс сериализации: процесс сериализации по умолчанию будет включен для классов, реализующих
Serializable
интерфейс. Определенный программистом процесс сериализации будет запущен для классов, реализующихExternalizable
интерфейс. - Обслуживание: несовместимые изменения могут нарушить сериализацию.
- Обратная совместимость и контроль: если вам нужно поддерживать несколько версий, вы можете получить полный контроль с
Externalizable
интерфейс. Вы можете поддерживать разные версии вашего объекта. Если вы реализуетеExternalizable
это ваша ответственность за сериализациюsuper
учебный класс - общедоступный конструктор No-arg:
Serializable
использует отражение для конструирования объекта и не требует никакого конструктора arg. НоExternalizable
требует публичного конструктора без аргументов.
Обратитесь к блогу Hitesh Garg
Больше подробностей.
Просто для полноты transient
Ключевое слово также закрывает разрыв между ними.
Если вы хотите сериализовать только часть вашего объекта, просто установите определенные поля как transient
, помечая их как не подлежащие сохранению, и реализуйте Serializable
,
Сериализация использует определенные поведения по умолчанию для хранения и последующего воссоздания объекта. Вы можете указать, в каком порядке или как обрабатывать ссылки и сложные структуры данных, но в конечном итоге все сводится к использованию поведения по умолчанию для каждого примитивного поля данных.
Экстернализация используется в тех редких случаях, когда вы действительно хотите хранить и перестраивать свой объект совершенно другим способом и без использования стандартных механизмов сериализации для полей данных. Например, представьте, что у вас есть собственная уникальная схема кодирования и сжатия.
Сериализация объектов использует интерфейсы Serializable и Externalizable. Объект Java только сериализуем. если класс или любой из его суперклассов реализует либо интерфейс java.io.Serializable, либо его подынтерфейс, java.io.Externalizable. Большинство классов Java являются сериализуемыми.
NotSerializableException
:packageName.ClassName
"Для участия объекта Class в процессе сериализации, класс должен реализовывать интерфейс Serializable или Externalizable.
Сериализация объектов создает поток с информацией о классах Java для сохраняемых объектов. Для сериализуемых объектов хранится достаточно информации для восстановления этих объектов, даже если присутствует другая (но совместимая) версия реализации класса. Интерфейс Serializable определен для идентификации классов, которые реализуют сериализуемый протокол:
package java.io;
public interface Serializable {};
- Интерфейс сериализации не имеет методов или полей и служит только для определения семантики сериализации. Для сериализации / десериализации класса мы можем использовать методы writeObject и readObject по умолчанию (или) мы можем переопределить методы writeObject и readObject из класса.
- JVM будет полностью контролировать сериализацию объекта. используйте временное ключевое слово, чтобы предотвратить сериализацию элемента данных.
- Здесь сериализуемые объекты восстанавливаются непосредственно из потока без выполнения
InvalidClassException
"В процессе десериализации, если значение локального класса serialVersionUID отличается от соответствующего класса отправителя. тогда результат в конфликте, какjava.io.InvalidClassException: com.github.objects.User; local class incompatible: stream classdesc serialVersionUID = 5081877, local class serialVersionUID = 50818771
- Значения непереходных и нестатических полей класса сериализуются.
Для объектов Externalizable контейнер сохраняет только идентичность класса объекта; класс должен сохранить и восстановить содержимое. Интерфейс Externalizable определяется следующим образом:
package java.io;
public interface Externalizable extends Serializable
{
public void writeExternal(ObjectOutput out)
throws IOException;
public void readExternal(ObjectInput in)
throws IOException, java.lang.ClassNotFoundException;
}
- Интерфейс Externalizable имеет два метода: объект externalizable должен реализовывать методы write External и readExternal для сохранения / восстановления состояния объекта.
- Программист должен позаботиться о том, какие объекты сериализуются. Как программист позаботится о сериализации Итак, здесь временное ключевое слово не будет ограничивать какой-либо объект в процессе сериализации.
- Когда объект Externalizable восстанавливается, экземпляр создается с использованием открытого конструктора no-arg, а затем вызывается метод readExternal. Сериализуемые объекты восстанавливаются путем чтения их из ObjectInputStream.
OptionalDataException
"Поля ДОЛЖНЫ БЫТЬ В ОДНОМ ПОРЯДКЕ И ТИПЕ, как мы их выписали. Если есть какое-либо несоответствие типа из потока, он генерирует исключение OptionalDataException.@Override public void writeExternal(ObjectOutput out) throws IOException { out.writeInt( id ); out.writeUTF( role ); out.writeObject(address); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.id = in.readInt(); this.address = (Address) in.readObject(); this.role = in.readUTF(); }
Поля экземпляра класса, в котором написано (выставлено)
ObjectOutput
получить сериализованный.
Пример " реализует Сериализуемый
class Role {
String role;
}
class User extends Role implements Serializable {
private static final long serialVersionUID = 5081877L;
Integer id;
Address address;
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
}
class Address implements Serializable {
private static final long serialVersionUID = 5081877L;
String country;
}
Пример " реализует Externalizable
class User extends Role implements Externalizable {
Integer id;
Address address;
// mandatory public no-arg constructor
public User() {
System.out.println("Default Constructor get executed.");
}
public User( String role ) {
this.role = role;
System.out.println("Parametarised Constructor.");
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt( id );
out.writeUTF( role );
out.writeObject(address);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.address = (Address) in.readObject();
this.role = in.readUTF();
}
}
пример
public class CustomClass_Serialization {
static String serFilename = "D:/serializable_CustomClass.ser";
public static void main(String[] args) throws IOException {
Address add = new Address();
add.country = "IND";
User obj = new User("SE");
obj.id = 7;
obj.address = add;
// Serialization
objects_serialize(obj, serFilename);
objects_deserialize(obj, serFilename);
// Externalization
objects_WriteRead_External(obj, serFilename);
}
public static void objects_serialize( User obj, String serFilename ) throws IOException{
FileOutputStream fos = new FileOutputStream( new File( serFilename ) );
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
// java.io.NotSerializableException: com.github.objects.Address
objectOut.writeObject( obj );
objectOut.flush();
objectOut.close();
fos.close();
System.out.println("Data Stored in to a file");
}
public static void objects_deserialize( User obj, String serFilename ) throws IOException{
try {
FileInputStream fis = new FileInputStream( new File( serFilename ) );
ObjectInputStream ois = new ObjectInputStream( fis );
Object readObject;
readObject = ois.readObject();
String calssName = readObject.getClass().getName();
System.out.println("Restoring Class Name : "+ calssName); // InvalidClassException
User user = (User) readObject;
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void objects_WriteRead_External( User obj, String serFilename ) throws IOException {
FileOutputStream fos = new FileOutputStream(new File( serFilename ));
ObjectOutputStream objectOut = new ObjectOutputStream( fos );
obj.writeExternal( objectOut );
objectOut.flush();
fos.close();
System.out.println("Data Stored in to a file");
try {
// create a new instance and read the assign the contents from stream.
User user = new User();
FileInputStream fis = new FileInputStream(new File( serFilename ));
ObjectInputStream ois = new ObjectInputStream( fis );
user.readExternal(ois);
System.out.format("Obj[Id:%d, Role:%s] \n", user.id, user.role);
Address add = (Address) user.address;
System.out.println("Inner Obj : "+ add.country );
ois.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@увидеть
https://docs.oracle.com/javase/8/docs/platform/serialization/spec/serialTOC.html
Сериализация по умолчанию несколько многословна и предполагает максимально широкий сценарий использования сериализованного объекта, и, соответственно, формат по умолчанию (Serializable) аннотирует результирующий поток информацией о классе сериализованного объекта.
Экстернализация дает производителю объектного потока полный контроль над точными метаданными класса (если таковые имеются) за пределами минимальной требуемой идентификации класса (например, его имени). Это явно желательно в определенных ситуациях, например, в закрытых средах, в которых сопоставляется производитель потока объекта и его потребитель (который получает объект из потока), а дополнительные метаданные о классе не служат цели и ухудшают производительность.
Кроме того (как указывает Ури), экстернализация также обеспечивает полный контроль над кодированием данных в потоке, соответствующем типам Java. Для (надуманного) примера вы можете записать логическое true как "Y" и false как "N". Экстернализация позволяет вам сделать это.
Интерфейс Externalizable фактически не был предоставлен для оптимизации производительности процесса сериализации! но обеспечить средства для реализации вашей собственной пользовательской обработки и предложить полный контроль над форматом и содержимым потока для объекта и его супертипов!
Примерами этого является реализация удаленного взаимодействия AMF (ActionScript Message Format) для передачи собственных объектов сценария действия по сети.
Существует так много различий между Serializable и Externalizable, но когда мы сравниваем разницу между настраиваемыми Serializable(переопределенными writeObject() и readObject()) и Externalizable, мы обнаруживаем, что пользовательская реализация тесно связана с классом ObjectOutputStream, где, как и в случае Externalizable, мы сами предоставить реализацию ObjectOutput, которая может быть классом ObjectOutputStream или другой, например org.apache.mina.filter.codec.serialization.ObjectSerializationOutputStream
В случае интерфейса Externalizable
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(key);
out.writeUTF(value);
out.writeObject(emp);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.key = in.readUTF();
this.value = in.readUTF();
this.emp = (Employee) in.readObject();
}
**In case of Serializable interface**
/*
We can comment below two method and use default serialization process as well
Sequence of class attributes in read and write methods MUST BE same.
// below will not work it will not work .
// Exception = java.io.StreamCorruptedException: invalid type code: 00\
private void writeObject(java.io.ObjectOutput stream)
*/
private void writeObject(java.io.ObjectOutputStream Outstream)
throws IOException {
System.out.println("from writeObject()");
/* We can define custom validation or business rules inside read/write methods.
This way our validation methods will be automatically
called by JVM, immediately after default serialization
and deserialization process
happens.
checkTestInfo();
*/
stream.writeUTF(name);
stream.writeInt(age);
stream.writeObject(salary);
stream.writeObject(address);
}
private void readObject(java.io.ObjectInputStream Instream)
throws IOException, ClassNotFoundException {
System.out.println("from readObject()");
name = (String) stream.readUTF();
age = stream.readInt();
salary = (BigDecimal) stream.readObject();
address = (Address) stream.readObject();
// validateTestInfo();
}
Я добавил пример кода, чтобы объяснить лучше. пожалуйста, проверьте вход / выход объекта case Externalizable. Они не связаны с какой-либо реализацией напрямую.
Где, как Outstream/Instream тесно связаны с классами. Мы можем расширить ObjectOutputStream/ObjectInputStream, но его будет немного сложно использовать.
Рассматривая варианты повышения производительности, не забывайте о пользовательской сериализации. Вы можете позволить Java делать то, что он делает хорошо, или, по крайней мере, достаточно хорошо, бесплатно, и предоставлять настраиваемую поддержку того, что он делает плохо. Обычно это намного меньше кода, чем полная поддержка Externalizable.
Сериализация в Java
Java предоставляет механизм, называемый Object Serialization, который позволяет пользователю сохранять структуру и данные объекта во внешнем файле. Этот файл содержит информацию, касающуюся объекта; тип объекта, тип членов и данные, которыми владеют эти члены.
Внешний интерфейс:
Как
Serializable
интерфейс,Externizable
также сохраняет объект со своими данными во внешний файл, который мы можем позже получить. Но есть некоторая разница:В сериализации все члены / переменные в объекте сериализуются во внешний файл, тогда как в Externalization мы можем конкретно указать, какие члены мы хотим сохранить, так как сериализация сериализует весь класс, нам не нужно писать методы для чтения и запись внешнего файла. Но
Externalizable
требует двух методов; а именноreadExternal()
а такжеwriteExternal()
Полный справочник: разница между сериализацией и экстернализацией
Некоторые отличия:
Для сериализации нет необходимости в конструкторе по умолчанию для этого класса, потому что Object, потому что JVM создает то же самое с помощью Reflection API. В случае Externalization конструктор без аргумента не требуется, потому что управление находится в руках программирования и позже назначает десериализованные данные объекту через сеттеры.
При сериализации, если пользователь хочет пропустить определенные свойства для сериализации, он должен пометить эти свойства как временные, наоборот, для экстернализации не требуется.
Когда ожидается поддержка обратной совместимости для любого класса, рекомендуется использовать Externalizable. Сериализация поддерживает сохранение объекта defaultObject, и если структура объекта нарушена, это вызовет проблемы при десериализации.
В принципе, Serializable
является интерфейсом маркера, который подразумевает, что класс безопасен для сериализации, и JVM определяет, как он сериализуется. Externalizable
содержит 2 метода, readExternal
а также writeExternal
, Externalizable
позволяет разработчику решить, как сериализовать объект, где Serializable
сериализует объекты по умолчанию.
Есть 3 причины использовать Externalizable вместо Serializable:
- Это намного быстрее (https://hazelcast.com/blog/comparing-serialization-options/)
- Он более детерминирован (в прошлом у меня были проблемы с ключами кэша, состоящими из полей коробочного типа, потому что в зависимости от того, кто создает объект (Cacheable/Hibernate/Proxies/AnonymousClasses), сериализованный массив байтов совместим, но различен, я думаю, из-за некоторых метаданных , что приводит к дублированию в некоторых кэшах).
- Это избавляет вас от необходимости определять серийныйVersionUid, по крайней мере, согласно Hazelcast https://docs.hazelcast.com/imdg/4.2/serialization/implementing-java-serializable#implementing-java-externalizable