Friday, 29 June 2012

serialVersionUID in Java Serialization

The serialVersionUID is used for the versioning of serializable objects.The serialization runtime associates with each serializable 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 a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException.


Explicitly declare a serialVersionUID

A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID" that must be static, final, and of type long:

 ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification.


Defects of default serialVersionUID


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 thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.

When should update explicit serialVersionUID ?

Do not change the value of this field in future versions, unless you are knowingly making changes to the class which will make it incompatible with old serialized objects. The new versions of Serializable classes may or may not be able to read old serialized objects;  it depends upon the nature of the change (5.6 Type Changes Affecting Serialization in Java Object Serialization Specification).


Compatible Changes - An Example:


Compatible changes to classes are those changes for which the guarantee of interoperability can be maintained.The compatible changes means stream written by some version of a class, when the stream is read back by the same version of the class,earlier version of the class or later version of the class,there is no loss of information or functionality.

The writer writes its data in the most suitable form and the receiver must interpret that information to extract the parts it needs and to fill in the parts that are not available.

Let us see one example.We have one Employee class(V-01) and we created a new version(V-02) of it by adding a new field 'dept' .

Adding a new filed is a compatible change.When the class being reconstituted has a field that does not occur in the stream, that field in the object will be initialized to the default value for its type. If class-specific initialization is needed, the class may provide a readObject method that can initialize the field to nondefault values.

We have explicitly declared the serialVersionUID for both the versions of  Employee class.Notice that serialVersionUID of both the versions are same.No need to change the serialVersionUID until there is an incompatible change.

The following code serialize an object of Employee V01 and store it in a file and then deserialize it using later version of Employee V02.

Employee.java ----->Version -01


An Employee class, here the serialVersionUID is declared explicitly and its value is generated using Eclipse.

package serialize;

import java.io.Serializable;

/**
 * Employee class Version-01
 *
 **/
class Employee implements Serializable {

 /**
  * Explicitly generated serialVersionUID.
  **/
 private static final long serialVersionUID = 5676693815966917752L;
 String empName;
 int empno;

 public Employee(String empName, int empno) {
  this.empName = empName;
  this.empno = empno;

 }

 public String toString() {
  return ("Employee Name =" + empName + " & Employee No =" + empno);
 }
}

Employee.java ----->Version -02

We have created a new version, V02 of Employee class by adding a new field 'dept' in the class.The value of serialVersionUID is same as that of V01 because there is no such incompatible changes in the new version.

package serialize;

import java.io.Serializable;

/**
 * Employee class Version-02
 *
 **/
class Employee implements Serializable {

 /**
  * Explicitly generated serialVersionUID.
  **/
 private static final long serialVersionUID = 5676693815966917752L;
 String empName;
 int empno;
 String dept;

 public Employee(String empName, int empno, String dept) {
  this.empName = empName;
  this.empno = empno;
  this.dept = dept;
 }

 public String toString() {
  return ("Employee Name =" + empName + " & Employee No =" + empno
    + " &Department =" + dept);
 }
}


Serialize an object of Employee -V01

Serializing an object of Employee class Version-01 using java.io.ObjectOutputStream class and storing in a file named employee.ser.

package serialize;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Sender {

 /**
  * Serializing an object of Employee v-01 to a file employee.ser
  **/
 public static void main(String[] args) {
  ObjectOutputStream out = null;
  try {
   out = new ObjectOutputStream(new FileOutputStream("employee.ser"));

   /** Create one Employee v01 object to serialize **/

   Employee emp = new Employee("John", 12345);

   /** Serialize the object emp and write to file employee.ser **/

   out.writeObject(emp);
   System.out
     .println("An object of Employee V01 is serialized successfully :).");

  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   try {
    out.close();

   } catch (IOException e) {
    e.printStackTrace();
   }
  }

 }

}


Deserialize an object of Employee V01 using later version Employee V02


Deserializing an object of Employee V01 stored in the employee.ser file using the later version Employee V02.

package serialize;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Receiver {

 /**
  * Deserialize an object of Employee V01 using later version Employee
  * V02
  * 
  * @throws ClassNotFoundException
  **/
 public static void main(String[] args) throws ClassNotFoundException {

  ObjectInputStream in = null;

  try {

   in = new ObjectInputStream(new FileInputStream("employee.ser"));

   /** Deserialize the object stored in the file employee.ver **/

   Employee emp = (Employee) in.readObject();

   System.out.println(emp);

  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   try {
    in.close();
   } catch (IOException e) {
    e.printStackTrace();
   }

  }
 }
}


The output will be 

Employee Name =John & Employee No =12345 &Department =null

Notice that,when the class being reconstituted has a field (here dept)that does not occur in the stream, that field in the object will be initialized to the default(here null) value for its type.


Incompatible Changes - An Example:


Incompatible changes to classes are those changes for which the guarantee of interoperability cannot be maintained.For example, we are creating an 3rd version of the Employee class by changing the data type of empno from int to String.This is an incompatible change because implicit type casting is impossible between int and String data types.

Employee.java ----->Version -03


package serialize;

import java.io.Serializable;

/**
 * Employee class Version-03
 *
 **/
class Employee implements Serializable {

 /**
  * Explicitly generated serialVersionUID.
  **/
 private static final long serialVersionUID = 6308425627775073000L;

 String empName;
 String empno;
 String dept;

 public Employee(String empName, String empno, String dept) {
  this.empName = empName;
  this.empno = empno;
  this.dept = dept;
 }

 public String toString() {
  return ("Employee Name =" + empName + " & Employee No =" + empno
    + " &Department =" + dept);
 }
}



Notice that we have changed the serialVersionUID of the Employee class version-03.Because changes to the class, change the data type of dept field from int to String,which will make it incompatible with old serialized objects.

Now let us see what will happen if we try to deserialize the old serialized object of Employee V-01 stored i the file 'employee.ser' using this later version of Employee class.

Deserialize an object of Employee V01 using later version Employee V03

package serialize;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Receiver {

 /**
  * Deserialize an object of Employee V01 using later version Employee
  * V03
  * 
  * @throws ClassNotFoundException
  **/
 public static void main(String[] args) throws ClassNotFoundException {

  ObjectInputStream in = null;

  try {

   in = new ObjectInputStream(new FileInputStream("employee.ser"));

   /** Deserialize the object stored in the file employee.ver **/

   Employee emp = (Employee) in.readObject();

   System.out.println(emp);

  } catch (IOException e) {
   e.printStackTrace();
  } finally {
   try {
    in.close();
   } catch (IOException e) {
    e.printStackTrace();
   }

  }
 }
}


The output will be 

You will get java.io.InvalidClassException.

java.io.InvalidClassException: serialize.Employee; local class incompatible: stream classdesc serialVersionUID = 5676693815966917752, local class serialVersionUID = 6308425627775073000
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:579)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1600)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1513)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1749)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
at serialize.Receiver.main(Receiver.java:25)

Notice that if the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException.


How to generate serialVersionUID


You can use JDK “serialver” or Eclipse IDE to generate serialVersionUID automatically.

Jdk tools provides a tool named serialver. Use serialver -show to start the gui version of the tool as shown below.




When you declare a class as Serializable you will get a warning message in your IDE

"The serializable class Employee does not declare a static final serialVersionUID field of type long"



Click on the warning indicator (marked with a red circle in the above screen shot) ,then you will get a link to generate the serialVersionUID.

No comments:

Post a Comment