Java Serialization – A Primer

Java serialization has been around since the Java version 1.1 days. This blog post just intends to be a refresher; brush the dust off the gray cells. So here goes.

First things first. Let’s create a class named NonSerializableClass. Here’s the source code:

package com.java.serialize;

public class NonSerializableClass {

}

Now let’s try to serialize it. Here’s the code to serialize NonSerializableClass.

public class NonSerializableTest {

	public static void main(String[] args) {
		
		NonSerializableClass nonSerializableObj = new NonSerializableClass();
		File f = new File("C:/obj.ser");
		
		try {
			FileOutputStream out = new FileOutputStream(f);
			BufferedOutputStream buf = new BufferedOutputStream(out);
			ObjectOutputStream objOut = new ObjectOutputStream(buf);
			objOut.writeObject(nonSerializableObj);
			
			out.close();
			buf.close();
			objOut.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

The package and import declarations have been removed for brevity. On running the program we encounter the following exception.

Not Serializable Class
java.io.NotSerializableException: com.java.serialize.NonSerializableClass

The first rule of serialization is

Not all classes are serializable.

For a class to be serialized it should implement Serializable interface. Here’s an example of a Serializable class.

public class SerializableClass implements Serializable {
	
	private static final long serialVersionUID = 123L;

	public SerializableClass() {
		System.out.println("Inside SerializableClass constructor");
	}
}

Now let’s try to serialize this. Here’s the serialization code.

public class SerializableTest {

	public static void main(String[] args) {
		SerializableClass serObj = new SerializableClass();
		
		try {	
			File f = new File("C:\\obj.ser");
			f.createNewFile();		
			System.out.println("Serializing Object");
		
			FileOutputStream out = new FileOutputStream(f);
			BufferedOutputStream buf = new BufferedOutputStream(out);
			ObjectOutputStream objOut = new ObjectOutputStream(buf);
			objOut.writeObject(serObj);
			
			objOut.close();
			buf.close();
			out.close();			
			System.out.println("Serialization complete.");
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

Serialization works fine without throwing any exceptions. The output of the class run is

Inside SerializableClass constructor
Serializing Object
Serialization complete.

Serialization rule number 2:

Only Classes implementing Serializable interface support serialization.

OK we were able to serialize the object, now let’s try to deserialize it. Here’s the source code.

public class DeSerializeObjectTest {

	public static void main(String[] args) {
		try {	
			File f = new File("C:\\obj.ser");
		
			System.out.println("Deserializing Object");
		
			FileInputStream input = new FileInputStream(f);
			BufferedInputStream buf = new BufferedInputStream(input);
			ObjectInputStream objip = new ObjectInputStream(buf);
			Object obj = objip.readObject();
			
			SerializableClass ser = (SerializableClass)obj; 
			
			objip.close();
			buf.close();
			input.close();
			
			System.out.println("Deserialization complete.");
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

The output is

Deserializing Object
Serialization complete.

So far so good, now let’s add an attribute to the class in question. Here’s the new source.

public class SerializableWithParamClass implements Serializable {

	private static final long serialVersionUID = 137L;
	
	private String name = null;

	public SerializableWithParamClass() {
		System.out.println("Inside SerializableWithParamClass constructor.");
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

Here’s the test class code.

public class SerializableWithParamTest {

	public static void main(String[] args) {
		
		SerializableWithParamClass param = new SerializableWithParamClass();
		param.setName("Testing.......");
		
		System.out.println("Begining the serialization process.");
		File f = new File("C:\\paramObj.ser");
		
		try {	
			
			f.createNewFile();
		
			System.out.println("Serializing Object");
		
			FileOutputStream out = new FileOutputStream(f);
			BufferedOutputStream buf = new BufferedOutputStream(out);
			ObjectOutputStream objOut = new ObjectOutputStream(buf);
			objOut.writeObject(param);
			
			objOut.close();
			buf.close();
			out.close();
			
			System.out.println("Serialization complete.");
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Ending the serialization process.");
		System.out.println("Begining the deserialization process.");
		try {	
				
			System.out.println("Deserializing Object");
		
			FileInputStream input = new FileInputStream(f);
			BufferedInputStream buf = new BufferedInputStream(input);
			ObjectInputStream objip = new ObjectInputStream(buf);
			Object obj = objip.readObject();
			
			SerializableWithParamClass ser = (SerializableWithParamClass)obj;
			
			System.out.println("Name: " + ser.getName());
			
			objip.close();
			buf.close();
			input.close();
			
			System.out.println("Deserialization complete.");
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		System.out.println("Ending the deserialization process.");

	}
}

The output is as follows:

Inside SerializableWithParamClass constructor.
Begining the serialization process.
Serializing Object
Serialization complete.
Ending the serialization process.
Begining the deserialization process.
Deserializing Object
Name: Testing.......
Deserialization complete.
Ending the deserialization process.

I am sure you have noticed by now the attribute serialVersionUID. What’s the need for having this attribute? Whenever object is serialized Java stores the java major and minor version information. Along with this it also needs to store some kind of version information,something which says that I am version 1.0 of this class, on addition/deletion of an attribute or method it should say that I am version 2.0. The serialVersionUID serves this purpose. The developer can define a hard coded value as I have done or allow Java can produce the value at runtime which would be based on the evaluation of the class.

So what affects the value of serialVersionUID and what does not?

What affects SerialVersionUID?

  1. Non-default Constructors
  2. Addition/Deletion of non-private static or instance methods as well as their access modifiers
  3. Addition/Deletion of static/instance attributes as well as their access modifiers
  4. The interfaces implemented by the class

What does not affect SerialVersionUID?

  1. Default Constructor
  2. Addition of private static or instance methods
  3. The class extended by the class to be serialized

One approach is to define a fixed value for serialVersionUID as I have done. The benefit is that if we have serialized an object and then added new attribute/method, as the serialVersionUID has not changed, the deserialization process is not hindered.

Let’s serialize an object of SerializableClass and change the serialVersionUID from 123L to 124L. Now on deserializing the object we encounter the following exception:

Deserializing Object
java.io.InvalidClassException: com.java.serialize.SerializableClass; local class incompatible: stream classdesc serialVersionUID = 123, local class serialVersionUID = 124

Now let’s examine the impact of serialization on object hierarchy.

public class A {
	
	private String name = null;

	public A() {
		System.out.println("Inside A's constructor.");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}	
}

public class B extends A implements Serializable {

	private String detail = null;
	
	public B() {
		System.out.println("Inside B's constructor.");
	}

	public String getDetail() {
		return detail;
	}

	public void setDetail(String detail) {
		this.detail = detail;
	}	
}

Now let’s serialize and deserialize it. Here’s the code.

public class InheritTest {

	public static void main(String[] args) {
		
		B b = new B();
		b.setName("Test");
		b.setDetail("Test Details");
		
		System.out.println("Begining the serialization process.");
		File f = new File("C:\\inheritObj.ser");
		
		try {	
			
			f.createNewFile();
		
			System.out.println("Serializing Object");
		
			FileOutputStream out = new FileOutputStream(f);
			BufferedOutputStream buf = new BufferedOutputStream(out);
			ObjectOutputStream objOut = new ObjectOutputStream(buf);
			objOut.writeObject(b);
			
			objOut.close();
			buf.close();
			out.close();
			
			System.out.println("Serialization complete.");
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("Ending the serialization process.");
		System.out.println("Begining the deserialization process.");
		try {	
				
			System.out.println("Deserializing Object");
		
			FileInputStream input = new FileInputStream(f);
			BufferedInputStream buf = new BufferedInputStream(input);
			ObjectInputStream objip = new ObjectInputStream(buf);
			Object obj = objip.readObject();
			
			B bSer = (B)obj;
			
			System.out.println("Name: " + bSer.getName());
			System.out.println("Detail: " + bSer.getDetail());
			
			objip.close();
			buf.close();
			input.close();
			
			System.out.println("Deserialization complete.");
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		System.out.println("Ending the deserialization process.");
	}
}

The output generated is:

Inside A's constructor.
Inside B's constructor.
Begining the serialization process.
Serializing Object
Serialization complete.
Ending the serialization process.
Begining the deserialization process.
Deserializing Object
Inside A's constructor.
Name: null
Detail: Test Details
Deserialization complete.
Ending the deserialization process.

Note that during deserialization, the constructor of A is invoked and the attribute value of name is null.

Now let’s try two options. Make A implement Serializable and let B continue to implement Serializable.

public class A implements Serializable{
	
	private String name = null;

	public A() {
		System.out.println("Inside A's constructor.");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	
}

public class B extends A implements Serializable {

	private String detail = null;
	
	public B() {
		System.out.println("Inside B's constructor.");
	}

	public String getDetail() {
		return detail;
	}

	public void setDetail(String detail) {
		this.detail = detail;
	}	
}

The output is

Inside A's constructor.
Inside B's constructor.
Begining the serialization process.
Serializing Object
Serialization complete.
Ending the serialization process.
Begining the deserialization process.
Deserializing Object
Name: Test
Detail: Test Details
Deserialization complete.
Ending the deserialization process.

It is in line with our expectations.

Now option two where A implements Serializable and B does not implement Serializable.

public class A implements Serializable{
	
	private String name = null;

	public A() {
		System.out.println("Inside A's constructor.");
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}	
}

public class B extends A {

	private String detail = null;
	
	public B() {
		System.out.println("Inside B's constructor.");
	}

	public String getDetail() {
		return detail;
	}

	public void setDetail(String detail) {
		this.detail = detail;
	}	
}

The output is

Inside A's constructor.
Inside B's constructor.
Begining the serialization process.
Serializing Object
Serialization complete.
Ending the serialization process.
Begining the deserialization process.
Deserializing Object
Name: Test
Detail: Test Details
Deserialization complete.
Ending the deserialization process.

The output remains unchanged.

Java Serialization rule no 3

In Object hierarchy the root class needs to implement Serializable interface to ensure serializable of all attributes within the object hierarchy.

That’s it from serialization desk.

Advertisements

3 thoughts on “Java Serialization – A Primer

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