Saturday, October 21, 2017


Serialization in JAVA:



Short story about serialization
After many years of hard work, Earth's scientists developed a robot who can help them in daily work. But this robot had fewer features than the robots developed by the scientists from planet Mars.
After a meeting between both planets' scientists, it is decided that Mars will send their robots to Earth. But a problem occurred. The cost of sending 100 robots to Earth was $100 million. And it takes around 60 days of traveling.
Finally, Mar's scientists decided to share their secret with Earth's scientists. This secret was about the structure of class/robot. Earth's scientists developed the same structure on Earth itself. Mar's scientists serialized the data of each robot and sent it to earth. Earth's scientists deserialized the data and fed it into each robot accordingly.
This process saved them time in communicating a massive amount of data.
Some of the robots were being used in some defensive work on Mars. So their scientists marked some crucial properties of those robots as transient before sending their data to Earth. Note that the transient property is set to null (in case of reference) or to the default value (in case of the primitive type) when the object gets deserialized.
One more point noticed by Earth's scientists is that Mars's scientists asked them to create some static variables to keep details about the environment. These details are used by some robots. But Mars's scientists don't share these details. Because Earth's environment was different from Mars' environment.
Even though knowing about the robot class structure and having serialized data Earth's scientist were not able to deserialize the data which can make robots working.
Exception in thread "main" java.io.InvalidClassException:
SerializeMe; local class incompatible: stream classdesc
:
Mars's scientists were waiting for the complete payment. Once the payment was done Mars's scientists shared the serialversionUID with Earth's scientists. Earth's scientist set it to robot class and everything started working.

Java provides a mechanism to convert live object to bytestream and then save it to objects and back and forth.This process is called serialization.To achieve serialization java provides a Marker Interface Serializable(Interface having no methods and fields) to achieve serialization.Any Class that implements Serializable can be serialized and deserialized.

Below is the example of a simple java bean class that achieve serialization.
package com.kunal.serialization;

import java.io.Serializable;

public class SerializedStudent implements Serializable {

/**
* For versioning of class.
*/
//private static final long serialVersionUID = 1L;
public SerializedStudent(int aRoll,String aName) {
// TODO Auto-generated constructor stub
this.roll = aRoll;
this.name = aName;
}
int roll;
String name;
//String pass;

/*public String getPass() {
return pass;
}
public void setPass(String pass) {
this.pass = pass;
}*/
public int getRoll() {
return roll;
}
public void setRoll(int roll) {
this.roll = roll;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}



package com.kunal.serialization;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;



public class Driver {

public static void main(String[] args) {
try {
//Writing object to outputstream with help of object output stream
FileOutputStream fo = new FileOutputStream(new File("serializedStudent"));
SerializedStudent student = new SerializedStudent(25, "KuchBhi");
ObjectOutputStream ostream = new ObjectOutputStream(fo);
ostream.writeObject(student);


FileInputStream iStream = new FileInputStream(new File("serializedStudent"));
ObjectInputStream iObjStream = new ObjectInputStream(iStream);
SerializedStudent readStudent = (SerializedStudent)iObjStream.readObject();
System.out.println(readStudent.getName());
} catch ( IOException | ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

}


Output:
KuchBhi
25

If from our studment BEAN we uncomment pass field and comment the Object outputStream Section(Reading the old serialized object into new updated class),
We will get below exception:
java.io.InvalidClassException: com.kunal.serialization.SerializedStudent; local class incompatible: stream classdesc serialVersionUID = -6360204280632258449, local class serialVersionUID = 651924956756896015
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1843)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2000)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at com.kunal.serialization.Driver.main(Driver.java:24)
This is becaues if we do not provide serialVersionUID java provides a default serialVersionUID as per its specification and the guidelines are very dependent on structure of the class.So originally our class has a serialVersionUID say x.
But when we added a filed pass the structure of class changed and java changed the default serialVersionUID.Hence when we tried to deserilaze older file (which was having older serialVersionUID ) and tried to desiralize wrt new updated class it gave us an error.

serialVersionUID is used to ensure that during deserialization the same class (that was used during serialize process) is loaded. This is a one line definition to explain why a serialVersionUID is used?

Apart from the above definition there are quite  a few things to learn from this serialVersionUID. As per javadocs, following is format of serialVersionUID:

serialVersionUID Syntax

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
  • serialVersionUID is a static final field. You can assign any number of your choice to it. Later I will explain the significance of these two statements.

Why serialVersionUID?

Lets start with annoying warning message you get in your IDE when you declare a class as Serializable.
The serializable class Lion does not declare a static final serialVersionUID field of type long
Most of us used to ignore this message as we always do for a warning. My general note is, always pay attention to the java warning messages. It will help you to learn a lot of fundamentals.

serialVersionUID is a must in serialization process. But it is optional for the developer to add it in java source file. If you are not going to add it in java source file, serialization runtime will generate a serialVersionUID and associate it with the class. The serialized object will contain this serialVersionUID along with other data.

Even though serialVersionUID is a static field, it gets serialized along with the object. This is one exception to the general serialization rule that, “static fields are not serialized”.

How serialVersionUID is generated?

serialVersionUID is a 64-bit hash of the class name, interface class names, methods and fields. Serialization runtime generates a serialVersionUID if you do not add one in source. Refer this link for the algorithm to generate serialVersionUID.
It is advised to have serialVersionUID as unique as possible. Thats why the java runtime chose to have such a complex algorithm to generate it.
If you want help in generating it, jdk tools provides a tool named serialver. Use serialver -show to start the gui version of the tool as shown below.



How serialVersionUID works?

When an object is serialized, the serialVersionUID is serialized along with the other contents.
Later when that is deserialized, the serialVersionUID from the deserialized object is extracted and compared with the serialVersionUID of the loaded class.
If the numbers do not match then, InvalidClassException is thrown.

Serialization and inheretence:
Case 1 : If SuperClass is serializable then subclass is automatically serializable.
For details please refer to my Git repo(Link Shared at the bottom)
Case 2 : If Super class is not serializable and subclass is serailizble
- In this case at the time of serilization JVM will allocate default values to the fields belonging to super class
- At the time of deserialization it will execute default non-arg consutructor(Which is a must in this secanrio to avoid runtime exception).
For examples please refer to gitrepo.
Case 3 : If super calss is serialized and in subclass we want to avoid such scenario then there is no direct way for the same and to achieve this we can throw exceptions from read/write utility methods.

Serialization in SingleTon
As we know singleton can be easily breached by using reflection and serialization.One of the way forword to preserve singleton is to use readResolve method.This method must not have call elsewher in the code.It must be called by java runtime only to identify and return singleton instance.

For Serializable and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing the readResolve method, a class can directly control the types and instances of its own instances being deserialized. The method is defined as follows:
        ANY-ACCESS-MODIFIER Object readResolve()
                throws ObjectStreamException;
The readResolve method is called when ObjectInputStream has read an object from the stream and is preparing to return it to the caller. ObjectInputStream checks whether the class of the object defines the readResolve method. If the method is defined, the readResolve method is called to allow the object in the stream to designate the object to be returned.
Externalizable
At sender end:
Externalization lets the programmer to customize the flow of serialization with two methods readExternal(Object obj) and writeExternal(Object obj).JVM gives priority to Externalization first and if the class being serialized has not implemented Externalized but Serialized then it user ObjectOutputStream to serialize.
At receiver end:
The object is constructed using No-Arg Constructor and the readExternal is called.Otherwise its reconstructed using ObjectInputStream in case its implementing Serializable.you must have no args contructor if you implement externalizable.
package com.kunal.serialization.externalization;



import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;



public class ExternalizedStudent implements Externalizable {




/**
* For versioning of class.
*/
private static final long serialVersionUID = 1L;

public ExternalizedStudent(int aRoll,String aName) {
this.roll = aRoll;
this.name = aName;
}

public ExternalizedStudent(){

}

int roll;
String name;
public int getRoll() {
return roll;
}
public void setRoll(int roll) {
this.roll = roll;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {



out.writeInt(roll);
out.writeObject(name);


}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {



roll = in.readInt();
name = (String)in.readObject();
}
}









Driver:
package com.kunal.serialization.externalization;

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

public class ExternalizableApp {

public static void main(String[] args) {

ExternalizedStudent student = new ExternalizedStudent(4,"kunal");
//Serialize
try
{
FileOutputStream fileOut = new FileOutputStream("student.ser");
ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
outStream.writeObject(student);
outStream.close();
fileOut.close();
}catch(IOException i)
{
i.printStackTrace();
}
//Deserialize
student = null;
try
{
FileInputStream fileIn =new FileInputStream("student.ser");
ObjectInputStream in = new ObjectInputStream(fileIn);
student = (ExternalizedStudent) in.readObject();
in.close();
fileIn.close();
}catch(IOException i)
{
i.printStackTrace();
return;
}catch(ClassNotFoundException c)
{
System.out.println("student class not found");
c.printStackTrace();
return;
}
System.out.println("Deserialized student...");
System.out.println("Name: " + student.getName());
System.out.println("roll: " + student.getRoll());
}

}
Inheritence in Externalization:

Case 1: What if super class does not implement Externalizable:

If Super class does not externd externalizable and child class does then child classs can still serialize fields in its writeExternal method.
Case 2: What if super class implements Externalizable:
In this case both super and sub class will have externilizable implemantation and hence both of them will have readExternal/writeExternal methods overridden so child class need explicit call to these methods with objectInput and objectOutput as parameters.

Please refer to my git-repo



No comments:

Post a Comment