Serialization tutorial – Part IV

You can go through the part III here, If you haven’t yet.

serialVersionUID

Let us now only serialize the object DerivedTestObject. So object will be converted into byte streams and saved into the file.

package Serialization;

public class SerializationTest {
       public static void main(String[] args) {
              DerivedTestObject tObj1 = new DerivedTestObject (100, "TestSerialization", new OtherObject());
              SerializeUtility utility = new SerializeUtility();

              // serialization
              utility.serialize(tObj1);
       }
}

Now, consider adding a new field to DerivedTestObject class. New field could be of any type. Lets add a String type new field as shown below:

package Serialization;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DerivedTestObject extends TestObject {

       OtherObject otherObject;
       String newField = "newField";

       DerivedTestObject(int i, String s, OtherObject oo) {
              super(i, s);
              otherObject = oo;
       }

       private void writeObject(ObjectOutputStream oos) throws IOException {
              oos.writeObject(name);
              oos.writeInt(number);
       }

       private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
              this.name = (String) ois.readObject();
              this.number = ois.readInt();
       }
}

Now let us try to reconstruct the object by deserializing the byte streams which have already been saved into the file

package Serialization;

public class SerializationTest {
       public static void main(String[] args) {
              DerivedTestObject tObj1 = new DerivedTestObject (10, "TestSerialization", new OtherObject());
              SerializeUtility utility = new SerializeUtility();

              // deserialization
              DerivedTestObject tObj2 = (DerivedTestObject)utility.deSerialize();

              System.out.println("Number: " + tObj2.number);
              System.out.println("Name: " + tObj2.name);
              System.out.println("OtherObject: " + tObj2.otherObject);
       }
}

We will get java.io.InvalidClassException like below:

java.io.InvalidClassException: Serialization.DerivedTestObject; local class incompatible: stream classdesc serialVersionUID = 4049949982777102676, local class serialVersionUID = 1063756054748732080
	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 Serialization.SerializeUtility.deSerialize(SerializeUtility.java:29)
	at Serialization.SerializationTest.main(SerializationTest.java:9)
Exception in thread "main" java.lang.NullPointerException
	at Serialization.SerializationTest.main(SerializationTest.java:11)

If you can take a look at the exception message which is shown as

java.io.InvalidClassException: Serialization.DerivedTestObject; local class incompatible: stream classdesc serialVersionUID = 4049949982777102676, local class serialVersionUID = 1063756054748732080

Here local class refers the original class we want to reconstruct an instance of. Stream class refers the byte streams of serialized class. They have different serialVersionUIDs and hence they are incompatible.

What is this serialVersionUID and How did it come into picture!!!

serialVersionUID is an unique number associated with the Serializable class and 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(serialized class), then deserialization will result in an InvalidClassException.

In simple terms, serialVersionUID is used to identify different versions of same class. It is like a version number.

We can explicitly declare a serialVersionUID in a Serializable class by declaring a field named “serialVersionUID” that must be static, final, and of type long:

static final long serialVersionUID = L;

 private static final long serialVersionUID = 3487495895819393L;

If we do not declare a serialVersionUID explicitly in a Serializable class then compiler generates a default serialVersionUID from hash value of that class.

Remember that serialVersionUID generation depends upon compiler implementation but not on JVM implementation. So if we compile the same java file with two different compilers we might end up with two class files with two different default serialVersionUIDs.

Before adding new field “newField” to DerivedTestObject class, compiler generates a default serialVersionUID as its not provided by the user explicitly and when we serialize an instance of DerivedTestObject, this serialVersionUID also gets saved along with the other members of DerivedTestObject  object.

After adding a new field “newField” to DerivedTestObject class, compiler again generates a default serialVersionUID as its not provided by the user explicitly and there is a structural change in the class. Here this generated serialVersionUID will be different from its previous serialVersionUID, why because the default serialVersionUID value calculation for that class based on the hash of the class name, interface class names, methods, and fields.

During deserialization, object needs to be created from the saved bytes stream. Here, the saved serialVersionUID will be compared to the serialVersionUID of the DerivedTestObject class which is the type for the object which we want to reconstruct. Since serialVersionUID is a static field we can access this field from the DerivedTestObject class to verify whether its equal to the saved serialVersionUID or not. Since both these serialVersionUIDs are different here, deserialization process not proceeded further and throws InvalidClassException as it found both objects are incompatible.

In order to get rid of this exception, We must need to explicitly declare a serialVersionUID in DerivedTestObject class before adding a new field as shown below:

package Serialization;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DerivedTestObject extends TestObject {

       private static final long serialVersionUID = 1L;
       OtherObject otherObject;

       DerivedTestObject(int i, String s, OtherObject oo) {
              super(i, s);
              otherObject = oo;
       }

       private void writeObject(ObjectOutputStream oos) throws IOException {
              oos.writeObject(name);
              oos.writeInt(number);
       }

       private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
              this.name = (String) ois.readObject();
              this.number = ois.readInt();
       }
}

Now If we serialize the object of type DerivedTestObject using SerilaizationTest.java, then explicitly declared serialVersionUID value 1L gets saved.

package Serialization;

public class SerializationTest {
       public static void main(String[] args) {
              DerivedTestObject tObj1 = new DerivedTestObject (10, "TestSerialization", new OtherObject());
              SerializeUtility utility = new SerializeUtility();

              // serialization
              utility.serialize(tObj1);
       }
}

Now we can add a new field to DerivedTestObject like below:

package Serialization;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class DerivedTestObject extends TestObject {

       private static final long serialVersionUID = 1L;
       OtherObject otherObject;
       String newField = "newField";

       DerivedTestObject(int i, String s, OtherObject oo) {
              super(i, s);
              otherObject = oo;
       }

       private void writeObject(ObjectOutputStream oos) throws IOException {
              oos.writeObject(name);
              oos.writeInt(number);
       }

       private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
              this.name = (String) ois.readObject();
              this.number = ois.readInt();
       }
}

Now deserialize the object with the SerializationTest.java as shown below:

package Serialization;

public class SerializationTest {
       public static void main(String[] args) {
              DerivedTestObject tObj1 = new DerivedTestObject (100, "TestSerialization", new OtherObject());
              SerializeUtility utility = new SerializeUtility();

              // deserialization
              DerivedTestObject tObj2 = (DerivedTestObject)utility.deSerialize();

              System.out.println("Number: " + tObj2.number);
              System.out.println("Name: " + tObj2.name);
              System.out.println("OtherObject: " + tObj2.otherObject);
              System.out.println("NewField: " + tObj2.newField);
       }
}

If we run the above class then we get the following output:

Number: 10
Name: TestSerialization
OtherObject: null
NewField: null

Look at the “NewField” value which is null. Since its a new field defined in the class but not saved during serialization and hence it takes the its data type default value. newField is of String type so it takes null as its value during deserialization.

In a similar way, If we delete a field from a class after it is serialized and serialVersionUID is explicitly provided in that class then during deserialization the deleted field from the serialized object will be discarded.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s