Sep 9, 2015

Basics Revisited | SerialVersionUID

SerialVersionUID is one among the favorite topics in Java interview. Its important for Java programmer to understand its concept, not only for the interview but to use serialization in java. We will start by understanding what is SerialVersionUID  and how it is generated, and then move forward to see some code examples.



SerialVersionUID
SerialVersionUID is used to ensure that during deserialization the same class (that was used during serialize process) is loaded. 
Syntax:
ANY-ACCESS-MODIFIER final static long serialVersionUID = < value>
serialVersionUID is a static final field. You can assign any number of your choice to it. 

How serialVersionUID is generated?
During object serialization, the default Java serialization mechanism writes the metadata about the object, which includes the class name, field names and types, and superclass. This class definition is stored as a part of the serialized object. This stored metadata enables the deserialization process to reconstitute the objects and map the stream data into the class attributes with the appropriate type.
Every time an object is serialized the java serialization mechanism automatically computes a hash value, if you do not provide one in source. ObjectStreamClass's computeSerialVersionUID() method passes the class name, sorted member names, modifiers, and interfaces to the secure hash algorithm (SHA), which returns a hash value. This value is the serialVersionUID of the class, also called suid.
So when the serialize object is retrieved, the JVM first evaluates the suid of the serialized class and compares the suid value with the one of the object. If the suid values match then the object is said to be compatible with the class and hence it is de-serialized. If not InvalidClassException exception is thrown.

However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations and can produce different serialVersionUID in different environments.

Demonstrate serialVersionUID

Initial class to be serialized has a serialVersionUID as 1L.
import java.io.Serializable;
 
public class Lion implements Serializable {
  private static final long serialVersionUID = 1L;
  private String sound;
 
  public Lion(String sound) {
    this.sound = sound;
  }
 
  public String getSound() {
    return sound;
  }
}
Test serialVersionUID:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
 
public class SerialVersionUIDTest {
  public static void main(String args[]) throws IOException, ClassNotFoundException {
    Lion leo = new Lion("roar");
    // serialize
    System.out.println("Serialization done.");
    FileOutputStream fos = new FileOutputStream("serial.out");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(leo);
 
    // deserialize
    FileInputStream fis = new FileInputStream("serial.out");
    ObjectInputStream ois = new ObjectInputStream(fis);
    Lion deserializedObj = (Lion) ois.readObject();
    System.out.println("DeSerialization done. Lion: " + deserializedObj.getSound());
  }
}

Output:

Serialization done.
DeSerialization done. Lion: roar
Now change serialVersionUID to 2L in Lion class.
  private static final long serialVersionUID = 2L;
Comment the “serialize” block (4 lines of code) in SerialVersionUIDTest. Now run it and you will get the following exception.
  1. Serialized Lion with serialVersionUID with 1L.
  2. Changed serialVersionUID to 2L and compiled and loaded the class.
  3. Deserialize the already serialized object and load it with the latest class.
  4. We get exception as serialVersionUID is not matching.
Exception in thread "main" java.io.InvalidClassException: Lion; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
                    at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
                    at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
                    at java.io.ObjectInputStream.readClassDesc(Unknown Source)
                    at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
                    at java.io.ObjectInputStream.readObject0(Unknown Source)
                    at java.io.ObjectInputStream.readObject(Unknown Source)
                    at SerialVersionUIDTest.main(SerialVersionUIDTest.java:21)


Compatible or Incompatible changes to a serializable class
Simple Examples of compatible changes are:
  • Addition of a new field or class will not affect serialization, since any new data in the stream is simply ignored by older versions. The newly added field will be set to its default values when the object of an older version of the class is un-marshalled.
  • The access modifiers change (like private, public, protected or default) is compatible since they are not reflected in the serialized object stream.
  • Changing a transient field to a non-transient field is compatible change since it is similar to adding a field.
  • Changing a static field to a non-static field is compatible change since it is also similar to adding a field.


Some Simple Examples of incompatible changes are:
  • Changing implementation from Serializable to Externalizable interface cannot be done since this will result in the creation of an incompatible object stream.
  •  Deleting an existing Serializable fields will cause a problem.
  • Changing a non-transient field to a transient field is incompatible change since it is similar to deleting a field.
  • Changing a non-static field to a static field is incompatible change since it is also similar to deleting a field.
  • Changing the type of an attribute within a class would be incompatible, since this would cause a failure when attempting to read and convert the original field into the new field.
  • Changing the package of class is incompatible. Since the fully-qualified class name is written as part of the object byte stream.

If no suid is present, inspite of making compatible changes, JVM generates new suid, thus resulting in an exception if prior release version object is used.
The only way to get rid of the exception is to recompile and deploy the application again. If we explicitly mention the suid, then if any of the mentioned compatible changes are made the class need not to be recompiled. But for incompatible changes there is no other way than to compile again.

Hope you liked the post. Connect at +Java Territory  to stay tuned with latest updates.

1 comment: