Serialization in Java
Dear reader,
I have discussed here below topics:
1) What is serialization and how it works?
2) Impact of SerialVersionUID in Java files and on serialization behaviour.
3) Impact of serialization and de-serialization on Super class and base classes.
4) How to make a class or package non-serializable as few of classes in Java like "Thread,
ServerSocket, Socket, DatagramSocket etc" can't be serialized. The reason these classes
are tightly coupled with Native OS implementation.
Java serialization has been around since the Java version 1.1 days. Serialization is a key/mechanism
of persisting a Java object for future use, so this persistence can be stored in File or DB anything.
But the storage of Java object is only possible if you convert an object into bits/bytes.
Hence Serialization is a mechanism to convert a Java object into bit/byte streams so that it can
be stored in either storage media or sent via network to another machine. Then the recipient or reader
need to De-Serialize the same bit/bytes pattern and get the actual object.
It is something like Sender is encrypting a Password string and sent over network and then Recipient
decrypts the same string and fetches the actual value.
Let’s create a basic class named NonSerializableClass.
-----------------------
package com.java.serialize;
public class NonSerializableClass {
}
-----------------------
Now let’s try to serialize it. Here’s the code to serialize NonSerializableClass.
-----------------------
import java.io.*;
public class NonSerializableTest {
public static void main(String[] args) {
NonSerializableClass nonSerializableObj = new NonSerializableClass();
try {
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("HoldSerial.txt"));
out.writeObject(nonSerializableObj);
out.close();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
-----------------------
On running the program we encounter the following exception.
Not Serializable Class
java.io.NotSerializableException: com.java.serialize.NonSerializableClass
Serialization rule number 1:
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"); //File name can be any thing.
f.createNewFile();
System.out.println("Serializing Object");
FileOutputStream out = new FileOutputStream(f); //Breaking one liner output stream into buffer.
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
Deserialization complete. //Note: Constructor didn't get invoked while deserializing.
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");
BufferedInputStream buf = new BufferedInputStream(new FileInputStream(f));
ObjectInputStream objip = new ObjectInputStream(buf);
Object obj = objip.readObject();
SerializableWithParamClass ser = (SerializableWithParamClass)obj;
System.out.println("Name: " + ser.getName());
objip.close();
buf.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:
01 Inside SerializableWithParamClass constructor.
02 Begining the serialization process.
03 Serializing Object
04 Serialization complete.
05 Ending the serialization process.
06 Begining the deserialization process.
07 Deserializing Object
08 Name: Testing.......
09 Deserialization complete.
10 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.
The reason to discuss: Suppose you have serialized your objects into a File and keep using after de-serialization.
After 2 years, you have changed your original source code and added/deleted some methods, attributes etc
and now serialized your objects again but keeping the older stored/serialized objects as it is with client.
Now if your old serialized objects need to de-serialize with new new code base, it won't work.
It will say java.io.InvalidClassException. I mean Version/SerialVersionUID is modified.
So what affects the value of serialVersionUID and what does not?
What affects SerialVersionUID change?
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. Java force such old serialized objects to de-serialize
using new source code if Version is same.
Let’s serialize an object of SerializableClass and then change the serialVersionUID from 123L to 124L
and then compile it.
Now on deserializing the old object we encounter the following exception:
1 Deserializing Object
2 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:
01 Inside A's constructor.
02 Inside B's constructor.
03 Begining the serialization process.
04 Serializing Object
05 Serialization complete.
06 Ending the serialization process.
07 Begining the deserialization process.
08 Deserializing Object
09 Inside A's constructor.
10 Name: null
11 Detail: Test Details
12 Deserialization complete.
13 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
01 Inside A's constructor.
02 Inside B's constructor.
03 Begining the serialization process.
04 Serializing Object
05 Serialization complete.
06 Ending the serialization process.
07 Begining the deserialization process.
08 Deserializing Object
09 Name: Test
10 Detail: Test Details
11 Deserialization complete.
12 Ending the deserialization process.
It is in line with our expectations. Super class constructor didn't invoke.
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
01 Inside A's constructor.
02 Inside B's constructor.
03 Begining the serialization process.
04 Serializing Object
05 Serialization complete.
06 Ending the serialization process.
07 Begining the deserialization process.
08 Deserializing Object
09 Name: Test
10 Detail: Test Details
11 Deserialization complete.
12 Ending the deserialization process.
The output remains unchanged. Super class constructor didn't invoke.
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.
-------------------------
At Last I am discussing how to avoid making a class serializable. Note: If your super class
doesn't implement Serializable, it can't be serialized but if a sub-class implements, then the whole object
hierarchy can be serialized again.
To avoid this we need to re-define "writeObject() and readObject()" methods of ObjectOutputStream and
ObjectInputStream. I am writing re-define because these methods are declared as final, so can't be overridden.
Below is the complete code:
--------------------
//Super class doesn't implement Serializable here but sub class does.
public class SuperClass {
public SuperClass(){
System.out.println("Super class constructor called");
}
}
---------------
//Sub class
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class BaseClass extends SuperClass implements Serializable {
public String name;
public BaseClass(){
System.out.println("Base class constructor called");
name="Deepak modi";
}
public static void main(String[] args) throws IOException,ClassNotFoundException {
BaseClass b=new BaseClass();
ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("HoldSerial.txt"));
out.writeObject(b);
System.out.println("Base class object serialized/stored in a file");
ObjectInputStream in=new ObjectInputStream(new FileInputStream("HoldSerial.txt"));
BaseClass bIn=(BaseClass)in.readObject();
System.out.println("Base class object de-serialized/read from file");
System.out.println("Object: "+bIn);
System.out.println("Object name value: "+bIn.name);
}
//Re-defining the method
private void readObject(ObjectInputStream o) throws IOException, ClassNotFoundException {
System.out.println("These objects can't be de-serialized");
throw new IOException("These objects can't be de-serialized");
}
private void writeObject(ObjectOutputStream o) throws IOException {
System.out.println("These objects can't be serialized");
throw new IOException("These objects can't be serialized");
}
public String toString() {
return name;
}
}
//Output when you run this main method:
Super class constructor called
Base class constructor called
These objects can't be serialized
Exception in thread "main" java.io.IOException: These objects can't be serialized
at BaseClass.writeObject(BaseClass.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:917)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1339)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1290)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1079)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:302)
at BaseClass.main(BaseClass.java:17)
//You uncomment the "readObject() and "writeObject()" and then see behavior:
I did that and here is the output:::::::::::
Super class constructor called
Base class constructor called
Base class object serialized/stored in a file
Super class constructor called
Base class object de-serialized/read from file
Object: Deepak modi
Object name value: Deepak modi
------------------------END-----------------------
Tuesday, February 8, 2011
Serialization in Java
Subscribe to:
Post Comments (Atom)
hmm ive been wondering about this! -> Java Classes and Objects quick explination video <- !
ReplyDelete