July 01, 2016

Serialization of Singleton class..

Serialization is a process by which you can save or transfer the state of an object by converting it to a byte stream. During deserialization, the object is re-created.

And with Singleton design pattern, we control object instantiation. With this creational design pattern we make sure that only single object gets created.

ALSO READ: Serialization Interview Questions and Answers for Experienced Developers

However,  when we serialize a singleton class and invoke deserialization multiple times we can end up with multiple objects of the singleton class. Even though constructor in singleton class is declared as private, deserialization process gets hold of the private constructor while recreating the object from the serialized data store.

ALSO READ: Design Patterns and Singleton class..

How do you prevent for creating another instance of Singleton during serialization?
We can achieve this by using readResolve() method.

The readResolve() method is called when ObjectInputStream 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() or not. If the method is defined, the readResolve() is called to allow the object in the stream to designate the object to be returned.

The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastException will be thrown when the type mismatch is discovered.

Let was take an example:

Create a singleton class
package com.khs.singletontest;
import java.io.ObjectStreamException;
import java.io.Serializable;
public class EmployeeSingleton implements Serializable
{
    private static EmployeeSingleton employee;
   
    /*private constructor*/
    private EmployeeSingleton(){}
   
    public static EmployeeSingleton getEmployeeSingletonInstance()
    {
        if(employee==null)
            employee=new EmployeeSingleton();
        return employee;
    }
}


Create a class, which serialize and then de-serialize EmployeeSingleton object

package com.khs.singletontest;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializeEmployee {
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException{
        EmployeeSingleton employee = EmployeeSingleton.getEmployeeSingletonInstance();

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connFactory.ser"));
        oos.writeObject(employee);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("connFactory.ser"));
        EmployeeSingleton factory1 = (EmployeeSingleton) ois.readObject();
        ois.close();

        ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("connFactory.ser"));
        EmployeeSingleton factory2 = (EmployeeSingleton) ois2.readObject();
        ois2.close();

        System.out.println("Instance reference of factory1 : "+ factory1.getEmployeeSingletonInstance());
        System.out.println("Instance reference of factory2 : "+ factory2.getEmployeeSingletonInstance());
        System.out.println("Instance reference of factory1 : "+ factory1);
        System.out.println("Instance reference of factory2 : "+ factory2);
    }
}


Output of SerializeEmployee is :
Instance reference of factory1 : com.khs.singletontest.EmployeeSingleton@de6ced
Instance reference of factory2 : com.khs.singletontest.EmployeeSingleton@de6ced
Instance reference of factory1 : com.khs.singletontest.EmployeeSingleton@c17164
Instance reference of factory2 : com.khs.singletontest.EmployeeSingleton@1fb8ee3


Now add readResolve() method in EmployeeSingleton :

private Object readResolve() throws ObjectStreamException {
        return employee;
    }


Now check the output of SerializeEmployee:
Instance reference of factory1 : com.khs.singletontest.EmployeeSingleton@de6ced
Instance reference of factory2 : com.khs.singletontest.EmployeeSingleton@de6ced
Instance reference of factory1 : com.khs.singletontest.EmployeeSingleton@de6ced
Instance reference of factory2 : com.khs.singletontest.EmployeeSingleton@de6ced


Difference between readObject and readResolve method in Serialization?

  • Both readObject and readResolve are in ObjectInputStream class.
  • The readObject method is used to deserialize an object from the stream. The readResolve method is called when objectInputStream has read an object from the stream and is preparing to return it to the caller.
  • readObject does not check the construction of object. The readResolve method is not invoked on the object until the object is fully constructed.
  • Each subclass of a serializable object may define its own readObject method. If a class does not implement the method, the default serialization provided by defaultReadObject will be used. When implemented, the class is only responsible for restoring its own fields, not those of its supertypes or subtypes. The values of every field of the object whether transient or not, static or not are set to the default value for the fields type.
  • 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. The object returned should be of a type that is compatible with all uses. If it is not compatible, a ClassCastException will be thrown when the type mismatch is discovered.

-K Himaanshu Shuklaa..

No comments:

Post a Comment