Monday, 8 July 2019

How Deserialization works in Java?


Hi Friends,
In this post, I'm going to explain concept and process of Deserialization.

Deserialization is a process by which the object previously serialized is reconstructed back into it’s original form. The input to the deserialization process is the stream of bytes which we get over the other end of network or we simply read it from the file system or database.

Question: What is written in this stream of bytes? 

Answer: It contains all the information about the instance which was serialized by serialization process. This information includes class’s meta data, type information of instance fields and values of instance fields as well.

This same information is needed when object is reconstructed back to a new object instance. 

While deserializing an object, the JVM reads its class metadata from the stream of bytes which specifies whether the class of an object implements either Serializable or Externalizable interface.



Note that for the deserialization to happen seamlessly, the byte code of a class, whose object is being deserialized, must be present within the JVM performing deserialization.Otherwise, the ClassNotFoundException is thrown.

During deserialization, JVM creates object without calling constructor.

Note: In deserialization process, all the parent classes of instance should be serializable and if any super class in hierarchy is not serializable then it must have a default constructor.

So while deserialization, the super most class is searched first until any non-serializable class is found. If all super classes are serializable then JVM end up reaching Object class itself and create an instance of Object class first.If in between searching the super classes, any class is found non-serializable then it’s default constructor will be used to allocate an instance in memory.



If any super class of instance to be deserialized is non-serializable and also doesn’t have a default constructor then the ’NotSerializableException’ is thrown by JVM.

Also, before continuing with the object reconstruction, the JVM checks to see if the serialVersionUID mentioned in the byte stream matches the serialVersionUID of the class of that object. If it doesn’t match then the ‘InvalidClassException’ is thrown.



So, till now we got the instance located in memory using one of superclass’s default constructor. Note that after this no constructor will be called for any class. After executing super class constructor, JVM read the byte stream and use instance’s meta data to set type information and other meta information of instance.

After the blank instance is created, JVM first set it’s static fields and then invokes the default readObject() method [if it’s not overridden, otherwise overridden method will be called] internally which is responsible for setting the values from byte stream to blank instance.

After the readObject() method is completed, the deserialization process is done and you are ready to work with new deserialized instance.



What is the use of serialVersionUID?


  • Java Runtime automatically associates with each class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization.
  • If the receiver has loaded a class for the object that has different serialVersionUID than that of the corresponding sender’s class, then deserialization will result in an InvalidClassException.
  • A serializable class can declare its own serialVersionUID explicitly with name “serialVersionUID"that must be of type static, final and long.
  • Any Access Modifier static final long serialVersionUID = 42L;
  • If this ID is not specified explicitly, then Java runtime associates default UID. 
  • It is highly recommended that we should explicitly define a serialVersionUID, as default UID is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization.
  • serialVersionUID represents the class version and we should increment it if the current version of our class is not backwards compatible with its previous version.
  • Automatically generated UID is generated based on class name, its implemented interfaces and all its public and protected members. Changing any of these in any way will change the serialVersionUID.
  • If later, we need to make changes in new class in such a way that it provides backward compatibility to older version of classes, then we should use java tool serialver to generate serialVersionUID on the old class, and explicitly set that on the new class.
  • If we make changes in some fields of class and forget to change the UID field, then the default mechanism (in.defaultReadObject()) will not detect any difference, and try to deserialize incompatible data. That can lead to hard to find errors.



Fast Facts:  

transient and static fields are ignored in serialization. After deserialization, transient fields and non-final static data will be null. Final static fields still have values since they are part of the class data.

ObjectOutputStream and ObjectInputStream are used in serialization and deserialization.

During serialization, you need to handle IOException and during deserialization, you need to handle IOException and ClassNotFoundException. So the deserialized class type must be in the class path.

Serialization and deserialization can be used for copying and cloning objects. It is slower than regular clone, but can produce a deep copy very easily.

No comments:

Post a Comment