1. Introduction
Serialization means to convert state of an object into a byte stream. Deserialization is the process of converting the serialized form of an object back into a copy of the object. A Singleton class is supposed to have only one instance. Using deserialization, it is possible to break this pattern. If you deserialize a serialized object, it will create a new instance and hence break the singleton pattern.
Lets see this with the help of an example. We’ll first create a singleton class.
import java.io.Serializable; // A typical Singleton class class Singleton implements Serializable { public static Singleton instance = new Singleton(); private Singleton() { // private constructor } }
We’ll now serialize the Singleton
class we created and will then deserialize.
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; public class BreakSingletonExample { public static void main(String[] args) { try { // instance1 created from Singleton Singleton instance1 = Singleton.instance; // serialize instance1 ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text")); out.writeObject(instance1); out.close(); // deserialize from file to object ObjectInput in = new ObjectInputStream(new FileInputStream("file.text")); Singleton instance2 = (Singleton) in.readObject(); in.close(); System.out.println("instance1 hashCode: " + instance1.hashCode()); System.out.println("instance2 hashCode: " + instance2.hashCode()); } catch (Exception e) { e.printStackTrace(); } } }
When you run the code, the output would look like this:
instance1 hashCode: 1639705018 instance2 hashCode: 1867083167
As you can observe, hashCode of both instances is different which should not be the case if both instances were same. This proves that serialization can break the singleton pattern.
2. Solution
The solution is to implement the readResolve()
method.
After the implementation the Singleton
class looks like the following:
import java.io.Serializable; // A typical Singleton class class Singleton implements Serializable { public static Singleton instance = new Singleton(); private Singleton() { // private constructor } // implement readResolve method protected Object readResolve() { return instance; } }
Now run the BreakSingletonExample
class again. The output would look like the following:
instance1 hashCode: 1639705018 instance2 hashCode: 1639705018
As you can observe, the hashcode of both the instances in the same.
3. The readResolve Method
According to the documentation:
For
Serializable
andExternalizable
classes, thereadResolve
method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing thereadResolve
method, a class can directly control the types and instances of its own instances being deserialized.The
readResolve
method is called whenObjectInputStream
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 thereadResolve
method. If the method is defined, thereadResolve
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, aClassCastException
will be thrown when the type mismatch is discovered.