Learnitweb

Prevent singleton pattern from serialization in Java

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 and Externalizable classes, the readResolve method allows a class to replace/resolve the object read from the stream before it is returned to the caller. By implementing the readResolve method, a class can directly control the types and instances of its own instances being deserialized.

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.