Singleton class is a class which will create and refer the same single object, no matter how many time you instantiate an object out of it.
Below are the steps you can follow to create the same:
- Have a private constructor to prevent Singleton object instantiation from outside of the class.
- Have a private, volatile and static instance inside the class.
- Add no setter methods so that no one can change the value of the instance.
- Have a static method to return the instance value.
- Inside the above static method use double check locking to assign the value to it.
class MySingleton{ private volatile static MySingleton instance; private MySingleton() { } public static MySingleton getMySingletonObject() { if(instance == null) { synchronized(MySingleton.class) { if(instance == null) { instance = new MySingleton(); } } } return instance; } }
Lets test whether our singleton class is really working. How to check that ? We know if 2 objects are same object then their hashcode value should be same.
From any client class:
public class Manager { public static void main(String[] args) { MySingleton instance1=MySingleton.getMySingletonObject(); MySingleton instance2=MySingleton.getMySingletonObject(); System.out.println("Hashcode of instance1: "+ instance1.hashCode()); System.out.println("Hashcode of instance2: "+ instance2.hashCode()); } }
The output:
Hashcode of instance1: 366712642 Hashcode of instance2: 366712642
How to make singleton class reflection proof ?
In the below code we have taken help from reflection to break the singleton pattern:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Manager { public static void main(String[] args) { MySingleton instance1=MySingleton.getMySingletonObject(); MySingleton instance2=null; try { Class<MySingleton> clazz = MySingleton.class; Constructor<MySingleton> cons = clazz.getDeclaredConstructor(); cons.setAccessible(true); instance2 = cons.newInstance(); } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) { e.printStackTrace(); } System.out.println("Hashcode of instance1: "+ instance1.hashCode()); System.out.println("Hashcode of instance2: "+ instance2.hashCode()); } }
The output:
Hashcode of instance1: 366712642 Hashcode of instance2: 1829164700
How to prevent the reflection ?
You have to modify the constructor like below:
private MySingleton() { //Prevent form the reflection api. if (instance != null){ throw new RuntimeException("Use getMySingletonObject() method to get the single instance of this class."); } }
It will throw an exception while someone tries to re-instantiates it.
How to make singleton Serialization proof?
Lets see what is Serialization and deserialization and how to do that:
class MySingleton implements Serializable{ private volatile static MySingleton instance; private MySingleton() { //Prevent form the reflection api. if (instance != null){ throw new RuntimeException("Use getMySingletonObject() method to get the single instance of this class."); } } public static MySingleton getMySingletonObject() { if(instance == null) { synchronized(MySingleton.class) { if(instance == null) { instance = new MySingleton(); } } } return instance; } } public class Manager { public static void main(String[] args) throws IOException , ClassNotFoundException{ MySingleton instance1=MySingleton.getMySingletonObject(); MySingleton instance2=null; ObjectOutput out = null; out = new ObjectOutputStream(new FileOutputStream("filename.ser")); out.writeObject(instance1); out.close(); //deserialize from file to object ObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser")); instance2 = (MySingleton) in.readObject(); in.close(); System.out.println("Hashcode of instance1: "+ instance1.hashCode()); System.out.println("Hashcode of instance2: "+ instance2.hashCode()); } }
Output:
Hashcode of instance1: 1550089733 Hashcode of instance2: 2003749087
To avoid the above you need to add a private readResolve() method in MySingleton class:
private Object readResolve() { return getMySingletonObject(); }
When an object is deserialized, the serialization mechanism checks if the class defines a readResolve() method. If it does, it invokes this method after the object is deserialized and uses the returned object instead of the deserialized one.
The full class would look like below:
class MySingleton implements Serializable{ private volatile static MySingleton instance; private MySingleton() { //Prevent form the reflection api. if (instance != null){ throw new RuntimeException("Use getMySingletonObject() method to get the single instance of this class."); } } public static MySingleton getMySingletonObject() { if(instance == null) { synchronized(MySingleton.class) { if(instance == null) { instance = new MySingleton(); } } } return instance; } //Make singleton from serialize and deserialize operation. protected Object readResolve() { return getMySingletonObject(); } }
How to make Singleton Cloning proof ?
Well it can break only if your MySingleton class has implemented the Cloneable interface, if it does not implement then no body can break it. Enjoy !!..
If your MySingleton class implements some other class which had implemented the Cloneable interface then what you can do ? You just override the clone() method and throw CloneNotSupportedException like below:
@Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }