Monday, December 23, 2013

serialVersionUID in Java Serialization

serialVersionUID is used to ensure that during deserialization the same class (that was used during serialize process) is loaded. This is a one line definition to explain why a serialVersionUID is used?

Apart from the above definition there are quite  a few things to learn from this serialVersionUID. As per javadocs, following is format of serialVersionUID:

serialVersionUID Syntax

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
  • serialVersionUID is a static final field. You can assign any number of your choice to it. Later I will explain the significance of these two statements.

Why serialVersionUID?

Lets start with annoying warning message you get in your IDE when you declare a class as Serializable.
The serializable class Lion does not declare a static final serialVersionUID field of type long
Most of us used to ignore this message as we always do for a warning. My general note is, always pay attention to the java warning messages. It will help you to learn a lot of fundamentals.

serialVersionUID is a must in serialization process. But it is optional for the developer to add it in java source file. If you are not going to add it in java source file, serialization runtime will generate a serialVersionUID and associate it with the class. The serialized object will contain this serialVersionUID along with other data.

Even though serialVersionUID is a static field, it gets serialized along with the object. This is one exception to the general serialization rule that, “static fields are not serialized”.

How serialVersionUID is generated?

serialVersionUID is a 64-bit hash of the class name, interface class names, methods and fields. Serialization runtime generates a serialVersionUID if you do not add one in source. Refer this link for the algorithm to generate serialVersionUID.
It is advised to have serialVersionUID as unique as possible. Thats why the java runtime chose to have such a complex algorithm to generate it.
If you want help in generating it, jdk tools provides a tool named serialver. Use serialver -show to start the gui version of the tool as shown below.

How serialVersionUID works?

When an object is serialized, the serialVersionUID is serialized along with the other contents.
Later when that is deserialized, the serialVersionUID from the deserialized object is extracted and compared with the serialVersionUID of the loaded class.
If the numbers do not match then, InvalidClassException is thrown.
If the loaded class is not having a serialVersionUID declared, then it is automatically generated using the same algorithm as before.

Strongly recommended to declare serialVersionUID

Javadocs says,
“the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization”
Now you know why we should declare a serialVersionUID.
Not only declaring a serialVersionUID is sufficient. You must do the following two things carefully. Otherwise it defeats the purpose of having the serialVersionUID.
serialVersionUID should be maintained. As and when you change anything in the class, you should upgrade the serailVersionUID.
Try your best to declare a unique serialVersionUID.

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)
This Core Java tutorial was posted on08/01/20

No comments: