Apr 12, 2015

Singleton with Serialization - What is importance of readresolve in Serialization in Java

Why do we need to make a class serializable ?

When we require an object to convert its state to a byte stream so that the byte stream can be reverted back into a copy of the object then we need to make that class serializable, so that instance of that class can be treated with special

How can we make a class serializable ?

In Java, a object is serializable if its class or any of its superclasses implements either the java.io.Serializable interface or its subinterface, java.io.Externalizable. Serializability of a class is enabled by the class implementing Serializable interface.

java.io.Serializable 
is only a marker interface which tells the Java platform that the object is serializable. Classes that do not implement this interface will not have any of their state serialized or deserialized.

For simplicity, in this post we will use Serializable interface.

What is Singleton class:- Only one instance of this class is created with respect to a given class loader which loads this class in JVM.
If a singleton class is loaded by multiple class loader, multiple instance of this singleton class can be created.  For more details about singleton design pattern refer Singleton Design Pattern.

Below discussion assumes Singleton class is loaded only by one class loader - One object creation allowed for this class.

Main agenda of this post is to analyse -
1. what happens when a singleton class implements serializable interface?
2. How does Singleton design pattern breaks its contarct of creating only one instance ?
3. How to prevent creation of multiple object of Singleton class when it is serializable ?

Lets start with how to make Singleton class with serializable -  by implementing  java.io.Serializable  interface. Below sample code creates singleton class (Bill Pugh Singleton with Holder Pattern) and implements serializable interface.

import java.io.Serializable;

/**
 *
 * @author devinline
 */
public class LazySingleton implements Serializable{
    private static final long serialVersionUID = 1L;
    private LazySingleton() {
    }

    private static class Singleton {

        private static final LazySingleton INSTANCE = 
new LazySingleton();
    }

    public static LazySingleton getInstance() {
        return Singleton.INSTANCE;
    }
    
}

Above Singleton instance is created lazily, only when it is actually used (method getInstance() is called). Inner static class is loaded when INSTANCE references LazySingleton's object.

How many object is created when LazySingleton object is serialized and deserialized ?

Now we will write a client program which uses Singleton object and does serilization and Deserialzation. Below sample program serializes object and compute Hashcode of that object followed by it deserializes stream and retrieves object and compute hashcode for this retrieved object.We notice from sample output is that - Both hashcode is different so both object are different. 

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;

/**
 *
 * @author devinline
 */
public class SerializedSingletonClient {

    public static void main(String[] args) throws FileNotFoundException, 
            IOException, ClassNotFoundException {
        LazySingleton singletonInstance = LazySingleton.getInstance();
        //Serialize singleton object
        try (ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
                "file.ser"))) {
            out.writeObject(singletonInstance);
        }
        System.out.println("Serialization stage :singletonInstance hashCode=" + 
                singletonInstance.hashCode());
        
        //Deserialize singleton object
        try (ObjectInput in = new ObjectInputStream(new FileInputStream(
                "file.ser"))) {
            singletonInstance = (LazySingleton) in.readObject();
        }
        System.out.println("Deserialization stage :singletonInstance hashCode=" + 
                singletonInstance.hashCode());

    }
}

Sample output of SerializedSingletonClient 
:- Hashcode is different, two different object created.
Serialization stage :singletonInstance hashCode=332475715
Deserialization stage :singletonInstance hashCode=1940813045

Since our expectation from singleton class is to create one object per classloader. But here two instance is created.

How to prevent creation of multiple object from serializable singleton class ?

Java Serialization provides a special hook in the form of method readResolve() which decides what object is returned by serialization.
In order to achieve that classes must implement this special method readResolve() and when
private method on the class being instantiated, readResolve() is called before returing object and from this method object returned can be overwritten and controlled which object should be returned.

i.e:- Classes that need to define a replacement when an instance of it is read from the stream should implement this special method with the exact signature. (Java Doc)

Below is modified Singleton class which implement readResolve() method. Now we run the singleton client again and we can verify that only object is created with this serialized singleton class from hashcode value.

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 *
 * @author devinline
 */
public class LazySingleton implements Serializable {

    private static final long serialVersionUID = 1L;

    private LazySingleton() {
    }

    private static class Singleton {

        private static final LazySingleton INSTANCE = 
                new LazySingleton();
    }

    public static LazySingleton getInstance() {
        return Singleton.INSTANCE;
    }
    
    //readresolve returns INSTANCE after deserialization 
    private Object readResolve() throws ObjectStreamException {
        return Singleton.INSTANCE;
    }
}

Sample output of SerializedSingletonClient 
:-  Hashcode value is same, Only one object is created.
Serialization stage : singletonInstance hashCode=348789395
Deserialization stage :singletonInstance hashCode=348789395

-------------- End of post---------------------

Reference :- https://docs.oracle.com/javase/7/docs/api/
Location: Hyderabad, Telangana, India