Sunday, May 9, 2010

Serialization


Serialization as we all know is a powerful feature of java. It converts the state of object in to stream of bytes.


So why we need to store the state of an object?

First thing that comes into my mind is Games.We all play games right? We want to save the state of some characters in the game so that we can restore it back in case of any mishap. How this happens? It’s all because of serialization.


Serialization is extensively used in RMI technology to transfer stream of bytes from stub to skeleton. This topic altogether demands another post. Serialization found its extensive use in implementation of JavaBeans also.


So How to make Object serializable in JAVA?

It’s simple. Just implement the Serializable interface. Serializable interface is a marker interface (Marker interface is one which doesn’t contain any method declaration .it’s just used to inform the JVM to treat the class in a specified manner.) If you try to serialize the object which is not serializable then it throws NotSerializableException. Most of the java built in classes like Date, ArrayList and String are serializable


The class ObjectOutputStream has a method writeObject() method which takes serializable object as parameter and converts object into a series of bytes. We can write these bytes to a file or to socket. Thanks to Decorator Design Pattern which makes ObjectOutputStrem class to keep away from the destination streams like FileOutputStream or something like that. What matters for the ObjectOutputStream is converting given serializable object to bytes. It doesn’t care whether you are writing those bytes to a file or to a socket. If you want to write bytes to a file pipe the ObjectOutputStream to FileOutputStream or if you want write it to Socket pipe it to Socket.getOutputStrem(). It’s so simple. Isn’t it?


The same applies to an ObjectInputStream. It has a method readObject() which converts series of bytes to object. It also doesn’t care whether the bytes are coming from file or from a socket. The return type of the readObject method is Object hence you need to type cast it back to the type it was.


By default java serializes all the instance variables of a class. What if you have instance variable which stores very sensitive information like passwords or something like that. Once again java comes for your help. It provides a modifier called transient. A transient instance variable can’t be serialized.


Static variables also can’t be serialized as they are not instance variables.

Below example illustrates the serialization

class SerializeMe implements Serializable
{
private int a=10;
private int b=20;
transient private int c=30;
public void saveMe() throws IOException
{
FileOutputStream fos=new FileOutputStream("C:\\saveObject.ser");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(this);
oos.close();
}

public void loadMe() throws IOException, ClassNotFoundException
{
FileInputStream fis=new FileInputStream("C:\\saveObject.ser");
ObjectInputStream ois=new ObjectInputStream(fis);
SerializeMe tempObject=(SerializeMe)ois.readObject();
System.out.println("a:"+ tempObject.a +" b:"+ tempObject.b +" c:" + tempObject.c);
}
}



public class SerializationDemo
{
public static void main(String[] args) throws IOException, ClassNotFoundException
{
SerializeMe serializableObject=new SerializeMe();
serializableObject.saveMe();
serializableObject.loadMe();
}
}


The output of the above example is show below
a:10 b:20 c:0

From the output it’s obvious that java initializes the transient variable with its default data type values.

Till now we just talked about serializing an object which contains primitive instance variables.

What if Serializable object contains references to other objects?

How the serialization goes in that case?

Serialization goes fine in this case also but the only condition is that the referred objects must also be serializable otherwise serialization fails. When object contains other objects then writeObject method traverses the object tree using the references and writes them all. If any one of the objects is not serializable then jvm throws NotSerializableException



Versioning


When you create a serializable class JVM automatically creates a constant called serialVersionUID. SerialVersionUID that is created by JVM is a hash of class name, sorted member names, modifiers, and interfaces. It uses secured hash algorithm (SHA) to compute SerialVersionUID. Programmer can give his own number instead of default one which gets generated by JVM. IDE’s like Eclipse or NetBeans also generates serialVersionUID for you.


Vehicle.java

public class Vehicle implements Serializable
{
private static final long serialVersionUID = 1L;
String model;
int speed;
BigDecimal price=null;
Vehicle()
{
model="Honda Civic";
speed=200;
price=new BigDecimal(1400000.00);
}
}

Serialize.java

public class Serialize
{
public static void main(String[] args) throws IOException, ClassNotFoundException
{
Vehicle Vehicle=new Vehicle();
FileOutputStream fos=new FileOutputStream("C:\\serveruid.ser");
ObjectOutputStream oos=new ObjectOutputStream(fos);
oos.writeObject(Vehicle);
oos.close();
}
}

Deserialize.java

public class Deserialize
{
public static void main(String[] args) throws IOException, ClassNotFoundException
{
FileInputStream fis=new FileInputStream("C:\\serveruid.ser");
ObjectInputStream ois=new ObjectInputStream(fis);
Vehicle tempObject=(Vehicle)ois.readObject();
System.out.println("Model:"+tempObject.model + " speed: " + tempObject.speed + " price: " + tempObject.price);
ois.close();
}
}

After serializing the Vehicle class by Serialize.java if you change serialVersionUID from 1L to 2L in Vehical.java and try to deserialize it using Deserialize.java you get InvalidClassException saying


Exception in thread "main" java.io.InvalidClassException: core.java.Vehicle; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

This might be useful as when you modify any serializable class change its serialVersionUID so that you’ll get above mentioned exception when you try to deserialize the incompatible class version.


Serialization Verses Writing an Object state to a Text File.

You can ask like when we can write object state to a file as a CSV or TSV why should we go for serialization? In that case parsing the text file and restoring the object state will be a tedious process as programmers need to take care of order of fields. Parsing will be pain sometimes. Won’t it be? But the good thing about it is these files can be parsed by other languages like c, c++. But with Java serialization you can write the object state with writeObject() method of ObjectOutputStream and reload object state with readObject() method of ObjectInputStream in one shot as java is known for making the things simpler.


serialver tool


Serialver is a tool that gets shipped along with JDK. This tool returns the serialversionUID for one or more classes.

Syntax: serialver [options] [class names]

The option –show pops up a small GUI interface to enter the class name and displays the serialversionUID of that class as show below.

C:\> serialver -show



It’s obvious from the above screen shot that tool requires a full class name
You can the serialver tool to get the serialversionUID in the command prompt as shown below

C:\> serialver core.java.Vehicle

core.java.Vehicle: static final long serialVersionUID = 1L;

The tool comes handy when you have a class (say Vehicle.class ) that you serialized long before for which you don’t know the serialverionUID. But now you have new version of Vehicle.class and now you want to deserialize the state of object of old Vehicle class. If newer version of Vehicle class doesn’t have the same serialversionUID you get InvalidClassException as mentioned above. So to avoid this get the serialversionUId of old Vehicle.class using the serialver tool and put it in the new version of Vehicle class and deserialize the contents.


HAPPY SERIALIZATION