Serialization allows storing the object in some data store and re create it later on. 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 is private, deserialization process gets hold of the private constructor while recreating the object from the serialized data store.
So can we avoid it.Yes we can avoid it.We will go through step by step and explain what needs to be done when we reconstruct the object from the serialized data store so that singleton behavior is not broken when object reconstruction happens.
Case-1: Serialization breaking singleton behavior
Here we are serializing the singleton instance and reading it multiple times.So we will see that INSTANCE reference is same,however multiple objects are created.
package com.salil.algo;
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 SerializationTest {
/**
* @param args
* @throws IOException
* @throws FileNotFoundException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ConnectionFactory INSTANCE=ConnectionFactory.getInstance();
//Here I am serializing the connection factory instance
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("connFactory.ser"));
oos.writeObject(INSTANCE);
oos.close();
//Here I am recreating the instance by reading the serialized object data store
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("connFactory.ser"));
ConnectionFactory factory1 = (ConnectionFactory) ois.readObject();
ois.close();
//I am recreating the instance AGAIN by reading the serialized object data store
ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("connFactory.ser"));
ConnectionFactory factory2 = (ConnectionFactory) ois2.readObject();
ois2.close();
//Lets see how we have broken the singleton behavior
System.out.println("Instance reference check->" +factory1.getInstance());
System.out.println("Instance reference check->" +factory2.getInstance());
System.out.println("=========================================================");
System.out.println("Object reference check->"+factory1);
System.out.println("Object reference check->"+factory2);
}
}
Output is as follows:-
Instance reference check->com.salil.algo.ConnectionFactory@763f5d
Instance reference check->com.salil.algo.ConnectionFactory@763f5d
=========================================================
Object reference check->com.salil.algo.ConnectionFactory@13a317a
Object reference check->com.salil.algo.ConnectionFactory@186768e
So it has created two objects and one static reference for INSTANCE.So that means if we read the serialized format of singleton object multiple times,we will create multiple objects.This is not what singleton object is supposed to do.So can we avoid it,Yes we can.We will discuss the same in the next use case.
Case-2: Serialization and singleton working properly
In order to make serialization and singleton work properly,we have to introduce readResolve() method in the singleton class.readResolve() method lets developer control what object should be returned on deserialization.
For the current ConnectionFactory singleton class,readResolve() method will look like this.
/**
* Special hook provided by serialization where developer can control what object needs to sent.
* However this method is invoked on the new object instance created by de serialization process.
* @return
* @throws ObjectStreamException
*/
private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
Output is as follows:-
Instance reference check->com.salil.algo.ConnectionFactory@13a317a
Instance reference check->com.salil.algo.ConnectionFactory@13a317a
=========================================================
Object reference check->com.salil.algo.ConnectionFactory@13a317a
Object reference check->com.salil.algo.ConnectionFactory@13a317a
So now serialization and singleton is working properly and it does not matter how many times we read the serialized format of singleton object.We will get the one instance.readResolve() did the trick