How to Avoid Breaking the Singleton Pattern in Java: Best Practices

In java, three techniques will break the singleton design pattern. In this tutorial, we will explore these three techniques, and also solutions to prevent singleton class breaking from these three techniques.

Singleton design pattern

Three different methods are

  1. Reflection
  2. Serialization
  3. Clone

Read: Singleton Design Pattern Implementation in Java with an Example

Reflection

The java reflection API examines or introspects and modifies the runtime behavior of the class,  property, and methods.

The following code snippet demonstrates how reflection API destroys singleton pattern

public final class Singleton {
	
	//static property
	private static Singleton singleton = null;
	
	//private constructor
	private Singleton() {}
	
	//static method
	public static Singleton getSingleton() {
		if(singleton == null) {
			singleton = new Singleton();
		}		
		return singleton;
	}
}
import java.lang.reflect.Constructor;

public class DriverClass {

	// Main method
	public static void main(String[] args) throws Exception {

		Singleton singletonOne = Singleton.getSingleton();
		Singleton singletonTwo = null;

		try {
			Constructor constructor = Singleton.class.getDeclaredConstructor();
			constructor.setAccessible(true);
			singletonTwo = (Singleton) constructor.newInstance();

		} catch (Exception e) {
			System.out.println(e);
		}

		System.out.println("Hashcode of singletonOne  - " + singletonOne.hashCode());
		System.out.println("Hashcode of singletonTwo  - " + singletonTwo.hashCode());

	}
}

Running the above code generates two hashcodes, hence break the singleton pattern.

Hashcode of singletonOne  - 366712642
Hashcode of singletonTwo  - 1829164700

How to overcome reflection?

There are many ways to overcome reflection in a singleton. One way is to use a private constructor. This will prevent other classes from instantiating the singleton. Another way is to use a static factory method. This will also prevent other classes from instantiating the singleton.

This code will prevent new instance creation from reflection API.

//private constructor
	private Singleton() {
		if(singleton != null) {
			 throw new RuntimeException("Instance already exist");
		}
	}

Serialization

Serialization breaks the singleton property. The Serialization saves an object of a byte stream into a file or sends it over the network.

Deserialize will convert byte stream into an instance of the object. This creates a new instance and breaks the singleton property.

The below code snippet illustrates the violating singleton property.

import java.io.Serializable;

public final class Singleton implements Serializable{
	
	//static property
	private static Singleton singleton = null;
	
	//private constructor
	private Singleton() {
		 if(singleton != null) { throw new RuntimeException("Instance already exist");
		 }		 
	}
	
	//static method
	public static Singleton getSingleton() {
		if(singleton == null) {
			singleton = new Singleton();
		}		
		return singleton;
	}
}
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 DriverClass {
	
	//Main method
	public static void main(String[] args) throws Exception {
		
		Singleton singletonOne = Singleton.getSingleton();
		Singleton singletonTwo = null;
		
		ObjectOutput out  = new ObjectOutputStream(new FileOutputStream("file.txt"));
		out.writeObject(singletonOne);
		out.close();
		
		ObjectInput in  = new ObjectInputStream(new FileInputStream("file.txt"));
		singletonTwo = (Singleton) in.readObject();
		in.close();
		
		System.out.println("Hashcode of singletonOne  - "+ singletonOne.hashCode());
		System.out.println("Hashcode of singletonTwo  - "+ singletonTwo.hashCode());		
	}
}

The above code generates two different hashcodes, hence breaks the singleton property.

Hashcode of singletonOne  - 1639705018
Hashcode of singletonTwo  - 1867083167

How to overcome the Serialization problem

To overcome this problem, override the readResolve() method in the singleton class

protected Object readResolve() { 
        return singleton; 
} 

Cloning

Cloning is a process of creating an exact copy of the original object by copying all the content of the original object. The clone method breaks the singleton design pattern.

The Bellow code illustrates the cloning process.

public class DriverClass {
	
	//Main method
	public static void main(String[] args) throws Exception {
		
		Singleton singletonOne = Singleton.getSingleton();
		Singleton singletonTwo = (Singleton) singletonOne.clone();
		
		System.out.println("Hashcode of singletonOne  - "+ singletonOne.hashCode());
		System.out.println("Hashcode of singletonTwo  - "+ singletonTwo.hashCode());		
	}
}
Hashcode of singletonOne  - 366712642
Hashcode of singletonTwo  - 1829164700

How to prevent the clone method?

Create CloneNotSupport class and implement a Cloneable interface.

public class CloneNotSupport implements Cloneable{
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
}

Extend Singleton class, with CloneNotSupport, override the clone() method, and throws an exception CloneNotSupportedException.

public final class Singleton extends CloneNot{
	
	//static property
	private static Singleton singleton = null;
	
	//private constructor
	private Singleton() {}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		
		return getSingleton();
	}
	
	//static method
	public static Singleton getSingleton() {
		if(singleton == null) {
			singleton = new Singleton();
		}		
		return singleton;
	}

}

The above code prevents the clone method to create a new instance. Throws CloneNotSupportedException if try to clone.

Full code implementation

import java.io.Serializable;

public class Singleton extends CloneNot implements Serializable{
	
	private static Singleton singleton = null;

	
	private Singleton() { 
		if(singleton != null) {
		 throw new RuntimeException("Instance already exist");
		}
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		
		return getSingleton();
	}
	
	protected Object readResolve() { 
        return singleton; 
    } 
}

Conclusion

The Singleton class must prevent reflection API, serialization, and clone methods from breaking the singleton property.

Please write a comment, if you find anything wrong or suggestion to improve about the topic.

Recommended:

Leave A Reply

Your email address will not be published.