- Serialization error in java
- Java
- Java
- Reproduce the java.io.NotSerializableException in Java
- Causes of the java.io.NotSerializableException in Java
- Fix the java.io.NotSerializableException in Java
- The Structure of NotSerializableException
- Constructors
- How to deal with the NotSerializableException
- Download the Eclipse Project
- References
- “Learn From Others Experience»
Serialization error in java
I have a little problem:
I wanted to test serialization on android (using eclipse) and found an example how to do it.
I know I need to implement «Serializable» in the class I want to serialize, I already did that and always get a java.io.NotSerializableException exception. Here’s the code:
public void Button(View view) throws IOException, ClassNotFoundException < ser test = new ser(); test.x = 204; test.y = 2843; test.speed = 12; test.direction = 1343; test.a = 493; test.b = 2323; test.c = 29489; test.d = 394; byte[] arr = serialize(test); ser res = (ser) deserialize(arr); >public static byte[] serialize(Object o) < ByteArrayOutputStream bos = new ByteArrayOutputStream(); try < ObjectOutput out = new ObjectOutputStream(bos); out.writeObject(o); //This is where the Exception occurs out.close(); // Get the bytes of the serialized object byte[] buf = bos.toByteArray(); return buf; >catch(IOException ioe) < Log.e("serializeObject", "error", ioe); //"ioe" says java.io.NotSerializableException exception return null; >> public static Object deserialize(byte[] b) < try < ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(b)); Object object = in.readObject(); in.close(); return object; >catch(ClassNotFoundException cnfe) < Log.e("deserializeObject", "class not found error", cnfe); return null; >catch(IOException ioe) < Log.e("deserializeObject", "io error", ioe); return null; >> class ser implements Serializable
I hope you can help me, I don’t know what I did wrong…
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.TextView;
Serialization in Java is a mechanism of writing the state of an object into a byte-stream. It is mainly used in Hibernate, RMI, JPA, EJB, and JMS technologies.
The reverse operation of serialization is called deserialization where byte-stream is converted into an object. The serialization and deserialization process is platform-independent, which means you can serialize an object in a platform and deserialize it on a different platform.
In Java, a NotSerializableException exception is thrown when an instance of a class must implement the Serializable interface. The exception is thrown by either the serialization runtime, or by the instance of the class. The argument for the NotSerializableException is the name of the class.
The NotSerializableException class extends the ObjectStreamException class, which is defined as the superclass of all exceptions specific to Object Stream classes. Also, the ObjectStreamException class extends the IOException which signals that an I/O exception has occurred.
Illustration:
java.io Class NotSerializableException java.lang.Object java.lang.Throwable java.lang.Exception java.io.IOException java.io.ObjectStreamException java.io.NotSerializableException
Note: All Implemented Interfaces are Serializable interface.
public class NotSerializableException extends ObjectStreamException
Let us discuss the constructors of this class before a
- NotSerializableException(): Constructs a NotSerializableException object.
- NotSerializableException(String classname): Constructs a NotSerializableException object with message string.
Java
public void setId(String id)
public static void main(String[] args)
= new FileOutputStream( «employee.dat» );
Employee obj = new Employee();
Errors in Code Exception in thread "main" java.security.AccessControlException: access denied ("java.io.FilePermission" "employee.dat" "write") at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:472) at java.base/java.security.AccessController.checkPermission(AccessController.java:897) at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:322) at java.base/java.lang.SecurityManager.checkWrite(SecurityManager.java:752) at java.base/java.io.FileOutputStream.(FileOutputStream.java:225) at java.base/java.io.FileOutputStream.(FileOutputStream.java:126) at NotSerializableExceptionExample.main(NotSerializableExceptionExample.java:21)
How to deal with the NotSerializableException
- The simplest solution is to find the class that throws the exception and makes it implement the Serializable interface. However, this may not be feasible if the class that throws the exception belongs to a third-party library.
- In case the class refers to non-serializable objects and these objects should not be serialized, then, you can declare these objects as transient. Once a field of a class is declared as transient, then, it is ignored by the serializable runtime.
Java
class Student implements Serializable
public Student( int id, String name)
public static void main(String args[])
Student s1 = new Student( 007 , «Test» );
= new FileOutputStream( «TestFile.txt» );
«Object stored successfully» );
Object stored successfully
- Reproduce the java.io.NotSerializableException in Java
- Causes of the java.io.NotSerializableException in Java
- Fix the java.io.NotSerializableException in Java
Today, we will reproduce the java.io.NotSerializableException while writing a program in Java. We will also understand what this error means, leading to its causes and solutions.
Reproduce the java.io.NotSerializableException in Java
Example Code ( Student.java file):
package com.serializertest; class Student private String studentId; public String getId() return studentId; > public void setId(String id) this.studentId = id; > >
The Student class is a helper class with a member variable named studentId . It also has member methods named getId() and setId() to get and set the id of a student.
Example Code ( SerializerTest.java file):
package com.serializertest; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializerTest public static void main(String[] args) throws IOException FileOutputStream fileOutputStream = new FileOutputStream("students.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); Student student = new Student(); student.setId("0001"); objectOutputStream.writeObject(student); objectOutputStream.close(); > >
The SerializerTest is our main class where we have the main() driver method. Inside the main() , we create an instance of the FileOutputStream class to create a file.
Similarly, we create another instance of the ObjectOutputStream class.
Then, we create an object of the Student class and call its setId() method by passing a string argument. Next, we use the object of the ObjectOutputStream class to write an object to the stream.
For that, we are using the writeObject() method.
After that, we close the stream using the close() method and run the program, but it gives us the following exception in the program’s output console.
Exception in thread "main" java.io.NotSerializableException: com.serializertest.Student
Why are we facing this? Let’s understand the error to find out its causes below.
Causes of the java.io.NotSerializableException in Java
It is essential to understand NotSerializableException and Serialization to find the causes.
Serialization is a mechanism in Java programming that we use to write an object’s state into the byte stream. In contrast, the reverse operation of it is called Deserialization .
The Serialization and Deserialization are platform-independent. It means both processes can be performed on different platforms.
We use Serialization in Remote Method Invocation ( RMI ) and Enterprise Java Beans ( EJB ). It is also used in Java Persistence API ( JPA ), Hibernate, etc.
The NotSerializableException is an exception that extends the ObjectStreamException class, defined as a superclass of all other exceptions specific to the Object Stream classes.
Additionally, the ObjectStreamException extends the IOException , which further signals that an I/O exception has been generated.
Now, we know the Serialization and NotSerializableException in Java. This discussion takes us to discover the reasons for NotSerializableException in Java.
- The NotSerializableException occurs when an instance of a class must implement a Serializable interface.
- We also get this exception either by an instance of a class or the serialization runtime. Remember, the argument of NotSerializableException is a name of a class.
- As per the documentation, the complete object graph requires to be serializable. The NotSerializableException also occurs if at least one of the fields does not implement the Serializable interface while attempting to serialize an instance of a class.
Fix the java.io.NotSerializableException in Java
We can fix the java.io.NotSerializableException in Java using the following solutions.
- Find the class that throws this exception and make it Serializable by implementing the Serializable interface. Remember, this solution might not be a good choice if a class that throws the NotSerializableException belongs to the third-party library.
- We can declare the objects as transient if the class refers to the non-serializable objects where these objects must not be serializable. The question is, why declare it as transient ? It is because the fields of the class declared as transient would be ignored by serializable runtime, and we will not get any exceptions.
- If we need its data and its third party, we can consider other means of serialization. For instance, XML, JSON, etc., where we can get the third-party objects serialized without changing their definitions.
In our case, implementing the Serializable fixed the java.io.NotSerializableException . See the following example code.
Example Code ( Students.java file):
package com.serializertest; import java.io.Serializable; class Student implements Serializable private String studentId; public String getId() return studentId; > public void setId(String id) this.studentId = id; > >
Example Code ( SerializerTest.java file):
package com.serializertest; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializerTest public static void main(String[] args) throws IOException FileOutputStream fileOutputStream = new FileOutputStream("students.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); Student student = new Student(); student.setId("0001"); objectOutputStream.writeObject(student); objectOutputStream.close(); > >
In this tutorial we will discuss about NotSerializableException in Java. The exception is thrown when an instance of a class must implement the Serializable interface. The exception is thrown by either the serialization runtime, or by the instance of the class. The argument of the NotSerializableException is the name of the class.
The NotSerializableException class extends the ObjectStreamException class, which is defined as the superclass of all exceptions specific to Object Stream classes. Also, the ObjectStreamException class extends the IOException which signals that an I/O exception has occurred.
Finally, the NotSerializableException exists since the 1.1 version of the Java Development Kit (JDK).
The Structure of NotSerializableException
Constructors
Creates an instance of the NotSerializableException class.
Creates an instance of the NotSerializableException class, using the specified string as message. The string argument indicates the name of the class that threw the error.
To begin with, the NotSerializableException can be thrown when a class does not implement the Serializable interface. A sample example is described below:
public class Pair < /** * The key (left) part of the pair. */ private K key; /** * The value (right) part of the pair. */ private V value; public Pair(K key, V value) < this.key = key; this.value = value; >/** * * @return, the pair's key. */ public K getKey() < return this.key; >/** * * @return, the pair's value. */ public V getValue() < return this.value; >/** * Tests if an instance of the Pair class is equal to a specified Object. */ @Override public boolean equals(Object o) < if(o instanceof Pair) < Pair pair = (Pair) o; return (this.key == pair.key && this.value == pair.value); >else return false; > /** * Creates a String representation of the Pair class. */ @Override public String toString() < return "Pair: "; >>
In this file, we defined the Pair class, as a Java template, which consists of two fields, key and value . Also, we defined the following methods:
Returns the key of the pair.
Returns the value of the pair.
Checks whether the specified object equals to this pair.
Returns a String representation of the Pair class.
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; public final class Serializer < /** * Converts an Object to a byte array. * * @param object, the Object to serialize. * @return, the byte array that stores the serialized object. */ public static byte[] serialize(T object) < ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutput out = null; try < out = new ObjectOutputStream(bos); out.writeObject(object); byte[] byteArray = bos.toByteArray(); return byteArray; >catch (IOException e) < e.printStackTrace(); return null; >finally < try < if (out != null) out.close(); >catch (IOException ex) < >try < bos.close(); >catch (IOException ex) < >> > /** * Converts a byte array to an Object. * * @param byteArray, a byte array that represents a serialized Object. * @return, an instance of the Object class. */ public static Object deserialize(byte[] byteArray) < ByteArrayInputStream bis = new ByteArrayInputStream(byteArray); ObjectInput in = null; try < in = new ObjectInputStream(bis); Object o = in.readObject(); return o; >catch (ClassNotFoundException | IOException e) < e.printStackTrace(); return null; >finally < try < bis.close(); >catch (IOException ex) < >try < if (in != null) in.close(); >catch (IOException ex) < >> > >
In this file, we defined the Serializer class, which contains the following two static methods:
- static byte[] serialize(T object)
Serializes the specified object in a byte array.
Deserializes the specified byte array to an instance of the Object class.
NotSerializableExceptionExample.java:
public class NotSerializableExceptionExample < public static void main(String[] args) < Pair pair = new Pair("Key1", 1); System.out.println("Trying to serialize the following object: " + pair); Serializer.serialize(pair); // This statement throws a NotSerializableException >>
In this file, we defined the main method of our application that aims to serialize an instance of the Pair class. However, the Pair class does not implement the Serializable interface and thus, the NotSerializableException is thrown.
A sample execution is shown below:
Trying to serialize the following object: Pair: java.io.NotSerializableException: main.java.Pair at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at main.java.Serializer.serialize(Serializer.java:24) at main.java.NotSerializableExceptionExample.main(NotSerializableExceptionExample.java:8)
Also, the NotSerializableException can be thrown when a class that implements the Serializable interface contains fields that are not serializable:
SerializableClass.java:
import java.io.Serializable; public class SerializableClass implements Serializable < private static final long serialVersionUID = 1420672609912364060L; private Pair pair = null; public SerializableClass(String key, Integer value) < this.pair = new Pair(key, value); >@Override public String toString() < return pair.toString(); >>
In this file, we defined the SerializableClass that contains a Pair field. The SerializableClass implements the Serializable interface, but it refers to the Pair class which doesn’t.
NotSerializableExceptionExample_v2.java:
public class NotSerializableExceptionExample_v2 < public static void main(String[] args) < SerializableClass sClass = new SerializableClass("Key1", 1); System.out.println("Trying to serialize the following object: " + sClass); Serializer.serialize(sClass); // This statement throws a NotSerializableException >>
A sample execution is shown below:
Trying to serialize the following object: Pair: java.io.NotSerializableException: main.java.Pair at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184) at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548) at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509) at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432) at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178) at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348) at main.java.Serializer.serialize(Serializer.java:24) at main.java.NotSerializableExceptionExample_v2.main(NotSerializableExceptionExample_v2.java:8)
How to deal with the NotSerializableException
- The simplest solution is to find the class that throws the exception and make it implement the Serializable interface. However, this may not be feasible if the class that throws the exception belongs to a third-party library.
- In case the class refers to non-serializable objects and these objects should not be serialized, then, you can declare these objects as transient . Once a field of a class is declared as transient , then, it is ignored by the serializable runtime. For example:
TransientExample.java:
import java.io.Serializable; public class TransientExample implements Serializable < private static final long serialVersionUID = 6128016096756071380L; private transient Pair pair = null; public TransientExample(String key, Integer value) < this.pair = new Pair(key, value); >@Override public String toString() < return pair.toString(); >>
In this file, we defined the TransientExample that contains a Pair field. The TransientExample implements the Serializable interface, but it refers to the Pair class which doesn’t. However, the reference is declared as transient and thus, the object can be serialized normally: NotSerializableExceptionExample_v3.java:
public class NotSerializableExceptionExample_v3 < public static void main(String[] args) < TransientExample ex = new TransientExample("key", 1); System.out.println("Trying to serialize the following object: " + ex); Serializer.serialize(ex); System.out.println("The " + ex + " object was successfully serialized!"); >>
Trying to serialize the following object: Pair: The Pair: object was successfully serialized!
Download the Eclipse Project
The Eclipse project of this example: NotSerializableExceptionExample.zip.
This was a tutorial about the NotSerializableException in Java.
Sotirios-Efstathios (Stathis) Maneas is a PhD student at the Department of Computer Science at the University of Toronto. His main interests include distributed systems, storage systems, file systems, and operating systems.
All these exceptions are related to serialization which occurred while doing serialization and deserialization of objects. These exceptions are subclasses of ObjectStreamException which is a subclass of IOException.
Pre-requisite:
- The class does not match the serialversionUID of the class in the stream.
- The class contains properties with invalid primitive data types.
- The Externalizable class doesn’t have a public no-arg constructor.
- The Serializable class can’t access the no-arg constructor of its closest non-serializable superclass.
- If the stream header is invalid.
- If control information not found.
- If control information is invalid.
- JDK 1.1.5 or fewer attempts to call readExternal on a PROTOCOL_VERSION_2 stream.
This exception has thrown if readObject state is invalid within the following ObjectInputStream methods:
- Java: transient Serialization
- Java: static Serialization
- Java: Externalizable/Custom Serialization
- Java: Object Serialization with Inheritance
- Java: Object Externalizable Serialization with Inheritance
- Java: Array and Collection Serialization
- Java: Serialization Exception Handling
References
- https://docs.oracle.com/javase/8/docs/platform/serialization/spec/exceptions.html
- https://docs.oracle.com/javase/7/docs/api/java/io/ObjectStreamException.html
“Learn From Others Experience»
Количество просмотров 118K
Сериализация (Serialization) — это процесс, который переводит объект в последовательность байтов, по которой затем его можно полностью восстановить. Зачем это нужно? Дело в том, при обычном выполнении программы максимальный срок жизни любого объекта известен — от запуска программы до ее окончания. Сериализация позволяет расширить эти рамки и «дать жизнь» объекту так же между запусками программы.
Дополнительным бонусом ко всему является сохранение кроссплатформенности. Не важно какая у вас операционная система, сериализация переводит объект в поток байтов, который может быть восстановлен на любой ОС. Если вам необходимо передать объект по сети, вы можете сериализовать объект, сохранить его в файл и передать по сети получателю. Он сможет восстановить полученный объект. Так же сериализация позволяет осуществлять удаленный вызов методов (Java RMI), которые находятся на разных машинах с, возможно, разными операционными системами, и работать с ними так, словно они находятся на машине вызывающего java-процесса.
Реализовать механизм сериализации довольно просто. Необходимо, чтобы ваш класс реализовывал интерфейс Serializable. Это интерфейс — идентификатор, который не имеет методов, но он указывает jvm, что объекты этого класса могут быть сериализованы. Так как механизм сериализации связан с базовой системой ввода/вывода и переводит объект в поток байтов, для его выполнения необходимо создать выходной поток OutputStream, упаковать его в ObjectOutputStream и вызвать метод writeObject(). Для восстановления объекта нужно упаковать InputStream в ObjectInputStream и вызвать метод readObject().
В процессе сериализации вместе с сериализуемым объектом сохраняется его граф объектов. Т.е. все связанные с этим объекто, объекты других классов так же будут сериализованы вместе с ним.
Рассмотри пример сериализации объекта класса Person.
import java.io.*; class Home implements Serializable < private String home; public Home(String home) < this.home = home; >public String getHome() < return home; >> public class Person implements Serializable < private String name; private int countOfNiva; private String fatherName; private Home home; public Person(String name, int countOfNiva, String fatherName, Home home) < this.name = name; this.countOfNiva = countOfNiva; this.fatherName = fatherName; this.home = home; >@Override public String toString() < return "Person'; > public static void main(String[] args) throws IOException, ClassNotFoundException < Home home = new Home("Vishnevaia 1"); Person igor = new Person("Igor", 2, "Raphael", home); Person renat = new Person("Renat", 2, "Raphael", home); //Сериализация в файл с помощью класса ObjectOutputStream ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream("person.out")); objectOutputStream.writeObject(igor); objectOutputStream.writeObject(renat); objectOutputStream.close(); // Востановление из файла с помощью класса ObjectInputStream ObjectInputStream objectInputStream = new ObjectInputStream( new FileInputStream("person.out")); Person igorRestored = (Person) objectInputStream.readObject(); Person renatRestored = (Person) objectInputStream.readObject(); objectInputStream.close(); //Сериализация с помощью класса ByteArrayOutputStream ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream2 = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream2.writeObject(igor); objectOutputStream2.writeObject(renat); objectOutputStream2.flush(); //Восстановление с помощью класса ByteArrayInputStream ObjectInputStream objectInputStream2 = new ObjectInputStream( new ByteArrayInputStream(byteArrayOutputStream.toByteArray())); Person igorRestoredFromByte = (Person) objectInputStream2.readObject(); Person renatRestoredFromByte = (Person) objectInputStream2.readObject(); objectInputStream2.close(); System.out.println("Before Serialize: " + "n" + igor + "n" + renat); System.out.println("After Restored From Byte: " + "n" + igorRestoredFromByte + "n" + renatRestoredFromByte); System.out.println("After Restored: " + "n" + igorRestored + "n" + renatRestored); >>
Before Serialize: Person Person After Restored From Byte: Person Person After Restored: Person Person
В данном примере класс Home создан для того чтобы продемонстрировать, что при сериализации объекта Person, с ним сериализуется и граф его объектов. Класс Home так же должен реализовывать интерфейс Serializable, иначе случится исключение java.io.NotSerializableException. Так же в примере описана сериализация с помощью класса ByteArrayOutputStream.
Из результатов выполнения программы можно сделать интересный вывод: при восстановлении объектов, у которых до сериализации была ссылка на один и тот же объект, этот объект будет восстановлен только один раз. Это видно по одинаковым ссылкам в объектах после восстановления:
After Restored From Byte: Person Person After Restored: Person Person
Однако, так же видно, что при выполнении записи двумя потоками вывода (у нас это ObjectInputStream и ByteArrayOutputStream), объект home будет создан заново, несмотря на то, что он уже был создан до этого в одном из потоков. Мы видим это по разным адресам объектов home, полученных в двух потоках. Получается, что если выполнить сериализацию одним выходным поток, затем восстановить объект, то у нас есть гарантия восстановления полной сети объектов без лишних дубликатов. Конечно, в ходе выполнения программы состояние объектов может измениться, но это на совести программиста.
Из примера так же видно, что при восстановлении объекта может возникнуть исключение ClassNotFoundException. С чем это связано? Дело в том, что мы легко можем сериализовать объект класса Person в файл, передать его по сети нашему товарищу, который может восстановить объект другим приложением, в котором класса Person попросту нет.
Своя сериализация. Как сделать?
Что делать, если вы хотите управлять сериализацией сами? Например, ваш объект хранит в себе логин и пароль пользователей. Вам необходимо сериализовать его для дальнейшей передачи его по сети. Передавать пароль в таком случае крайне ненадежно. Как решить эту задачу? Существует два способа. Первый, использовать ключевое слово transient. Второй, вместо реализации интереса Serializable использовать его расширение — интерфейс Externalizable. Рассмотрим примеры работы первого и второго способа для их сравнения.
Первый способ — Сериализация с использованием transient
import java.io.*; public class Logon implements Serializable < private String login; private transient String password; public Logon(String login, String password) < this.login = login; this.password = password; >@Override public String toString() < return "Logon'; > public static void main(String[] args) throws IOException, ClassNotFoundException < Logon igor = new Logon("IgorIvanovich", "Khoziain"); Logon renat = new Logon("Renat", "2500RUB"); System.out.println("Before: n" + igor); System.out.println(renat); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Externals.out")); out.writeObject(igor); out.writeObject(renat); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("Externals.out")); igor = (Logon) in.readObject(); renat = (Logon) in.readObject(); System.out.println("After: n" + igor); System.out.println(renat); >>
Before: Logon Logon After: Logon Logon
Второй способ — Сериализация с реализацией интерфейса Externalizable
import java.io.*; public class Logon implements Externalizable < private String login; private String password; public Logon() < >public Logon(String login, String password) < this.login = login; this.password = password; >@Override public void writeExternal(ObjectOutput out) throws IOException < out.writeObject(login); >@Override public String toString() < return "Logon'; > @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException < login = (String) in.readObject(); >public static void main(String[] args) throws IOException, ClassNotFoundException < Logon igor = new Logon("IgorIvanovich", "Khoziain"); Logon renat = new Logon("Renat", "2500RUB"); System.out.println("Before: n" + igor); System.out.println(renat); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Externals.out")); out.writeObject(igor); out.writeObject(renat); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("Externals.out")); igor = (Logon) in.readObject(); renat = (Logon) in.readObject(); System.out.println("After: n" + igor); System.out.println(renat); >>
Before: Logon Logon After: Logon Logon
Первое отличие двух вариантов, которое бросается в глаза это размер кода. При реализации интерфейса Externalizable нам необходимо переопределить два метода: writeExternal() и readExternal(). В методе writeExternal() мы указываем какие поля будут сериализованы и как, в readExternal() как их прочитать. При использовании слова transient мы явно указываем, какое поле или поля не нужно сериализовывать. Так же заметим, что во втором способе мы явно создали конструктор по умолчанию, причем публичный. Зачем это сделано? Давайте попробуем запустить код без этого конструктора. И посмотрим на вывод:
Before: Logon Logon Exception in thread "main" java.io.InvalidClassException: Logon; no valid constructor at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:169) at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:874) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2043) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431) at Logon.main(Logon.java:45)
Мы получили исключение java.io.InvalidClassException. С чем это связано? Если пройти по стек-трейсу можно выяснить, что в конструкторе класса ObjectStreamClass есть строчки:
Для интерфейса Externalizable будет вызван метод получения конструктора getExternalizableConstructor(), внутри которого мы через Reflection попробуем получить конструктор по умолчанию класса, для которого мы восстанавливаем объект. Если нам не удается его найти, или он не public, то мы получаем исключение. Обойти эту ситуацию можно следующим образом: не создавать явно никакого конструктора в классе и заполнять поля с помощью сеттеров и получать значение геттерами. Тогда при компиляции класса будет создан конструктор по умолчанию, который будет доступен для getExternalizableConstructor(). Для Serializable метод getSerializableConstructor() получает конструктор класса Object и от него ищет нужный класс, если не найдет, то получим исключение ClassNotFoundException. Выходит, что ключевое различие между Serializable и Externalizable в том, что первому не нужен конструктор для создания восстановления объекта. Он просто полностью восстановится из байтов. Для второго при восстановлении сначала будет создан объект с помощью конструктора в точке объявления, а затем в него будут записаны значения его полей из байтов, полученных при сериализации. Лично мне больше нравится первый способ, он гораздо проще. Причем, даже если нам нужно все таки задать поведение сериализации, мы можем не использовать Externalizable, а так же реализовать Serializable, добавив (не переопределив) в него методы writeObject() и readObject(). Но для того, чтобы они «работали» нужно точно соблюсти их сигнатуру.
import java.io.*; public class Talda implements Serializable < private String name; private String description; public Talda(String name, String description) < this.name = name; this.description = description; >private void writeObject(ObjectOutputStream stream) throws IOException < stream.defaultWriteObject(); System.out.println("Our writeObject"); >private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException < stream.defaultReadObject(); System.out.println("Our readObject"); >@Override public String toString() < return "Talda'; > public static void main(String[] args) throws IOException, ClassNotFoundException < Talda partizanka = new Talda("Partizanka", "Viiiski"); System.out.println("Before: n" + partizanka); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("Talda.out")); out.writeObject(partizanka); out.close(); ObjectInputStream in = new ObjectInputStream(new FileInputStream("Talda.out")); partizanka = (Talda) in.readObject(); System.out.println("After: n" + partizanka); >>
Before: Talda Our writeObject Our readObject After: Talda
Внутри наших добавленных методов вызываются defaultWriteObject() и defaultReadObject(). Они отвечают за сериализацию по умолчанию, как если бы она работала без добавленных нами методов.
На самом деле это только верхушка айсберга, если продолжить углубляться в механизм сериализации, то с высокой доли вероятности, можно отыскать еще нюансы, найдя которые мы скажем: «Сериализация… не все так просто».