Wednesday, January 13, 2016

Serialization in Java

ALL ABOUT SERIALIZABLE

Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.

The byte stream created is platform independent. So, the object serialized on one platform can be deserialized on a different platform.



Serializable is a marker interface. When an object has to be transferred over a network ( typically through rmi or EJB) or to persist the state of an object to a file, the object Class needs to implement Serializable interface. Implementing this interface will allow the object converted into bytestream and transfer over a network.

Which class compute serialVersionUID and Serialization mechanism ?

Everytime an object is serialized the java serialization mechanism automatically computes a hash value. ObjectStreamClass's computeSerialVersionUID() method passes the class name, sorted member names, modifiers, and interfaces to the secure hash algorithm (SHA), which returns a hash value.The serialVersionUID is also called suid.

So when the serilaize object is retrieved , the JVM first evaluates the suid of the serialized class and compares the suid value with the one of the object. If the suid values match then the object is said to be compatible with the class and hence it is de-serialized. If not InvalidClassException exception is thrown.

Below two important methods helps in serializing and deserializing object.

ObjectOutputStream.writeObject() // serialize and write to the stream
ObjectInputStream.readObject() // read and deserialize to the stream


public class SerializeObject implements Serializable{

     private static final long serialVersionUID = 1L;
     int a;
     // static variable won't affect value of serialized object
     static String b;
     String c;
     public static void main(String[] args) throws IOException
{

          SerializeObject serialObject = new SerializeObject();
          FileOutputStream fo = new FileOutputStream("testSer.ser");
          ObjectOutputStream os=new ObjectOutputStream(fo);
          os.writeObject(serialObject);
         
          serialObject = new SerializeObject();
          FileInputStream fi=new FileInputStream("testSer.ser");
          ObjectInputStream oi=new ObjectInputStream(fi);
          serialObject =(SerializeObject) oi.readObject();
          System.out.println(serialObject);
          oi.close();
     }
}

If serialVersionUID is different while writing and reading, then it’ll throw below exception
Exception in thread "main" java.io.InvalidClassException: com.javaetutorials.SerializeObject; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

Object Graph
In Java it wouldn't make any sense to save the actual value of a reference variable, because the value of a Java reference has meaning only within the context of a single instance of a JVM. In other words, if you tried to restore the object in another instance of the JVM, even running on the same computer on which the object was originally serialized, the reference would be useless.

If you serialize a Dog object, the Collar will be serialized automatically.
And if the Collar class contained a reference to another object, THAT object would also be serialized, and so on.

Note : Fortunately, the Java serialization mechanism takes care of all of this. When you serialize an object, Java serialization takes care of saving that object's entire "object graph." 

That means a deep copy of everything the saved object needs to be restored.

public class ObjectGraph {
     public static void main(String[] args) throws IOException, ClassNotFoundException {
          Collar c = new Collar(3);
          Dog d = new Dog(c, 8);
         
          FileOutputStream fos = new FileOutputStream("testSerDog.ser");
          ObjectOutputStream os = new ObjectOutputStream(fos);
          os.writeObject(d);
          os.close();
          System.out.println("After Serialization ,b is "+ d.b+" before: collar size is "+ d.getCollar().getCollarSize());
         
          FileInputStream fis = new FileInputStream("testSerDog.ser");
          ObjectInputStream ois = new ObjectInputStream(fis);
          d = (Dog) ois.readObject();
          ois.close();
          System.out.println("After deserializtion b value is "+ d.b);
          System.out.println("after: collar size is "+ d.getCollar().getCollarSize());
     }
}

   /*To save Dog and restore, Collar should also need to be save...
   Without making Collar as Serializable , it throws java.io.NotSerializableException: com.javaetutorials.Collar*/

class Dog implements Serializable {
    
     private static final long serialVersionUID = -8165365370689001304L;
    
/*    >If Collar is transient and when the Dog is deserialized,          it comes back with a null Collar.
       Thus it'll throw NullPointerException...To prevent it,            make Collar as static and it'll work...
      >If Collar is static , then it'll act as class variable ,
       so need to make Collar class serializable and it doesn't          participate in Serialization...
*/

     private transient Collar theCollar;
     private int dogSize;
     // if b variable is transient i.e its state is not saved ,when deserialize it'll come back as default value...
     public transient int b=10;
     public Dog(Collar collar, int size) {
          theCollar = collar;
          dogSize = size;
     }

     public Collar getCollar() {
          return theCollar;
     }
}

/*Collar must be serializable as it has reference in Dog Object...
  To prevent Collar to be serialized, use Transient keyword...
*/

class Collar implements Serializable {

     private static final long serialVersionUID = -8706017246615683290L;
     private int collarSize;

     public Collar(int size) {
          collarSize = size;
     }

     public int getCollarSize() {
          return collarSize;
     }
}

Output :
After Serialization ,b is 10 before: collar size is 3
After deserializtion b value is 0
Exception in thread "main" java.lang.NullPointerException
          at com.javaetutorials.ObjectGraph.main(ObjectGraph.java:28)

 To prevent NullPointerException, make Collar as static variable

Output : 
After Serialization ,b is 10 before: collar size is 3
After deserializtion b value is 0
after: collar size is 3

Another way to serialize objects :
·        Externalization implements Serialization
·        XML Serialization
·        Write an object's content directly via either the ObjectOutputStream or the DataOutputStream.

What happens if an object is serializable but it includes a reference to a non-serializable object?


Exception in thread "main" java.io.NotSerializableException: com.javaetutorials.Collar

1 comment:

  1. InvalidClassException - When suid is different during writing and reading of a stream.
    NotSerializableException- When object is serializable but it includes a reference to a non-serializable object.
    NullPointerException- When reference is transient and tried to deserialize

    ReplyDelete