How To Save Singleton Pattern from Reflection Serialization and Cloning
Posted By : Navin Purohit | 26-Apr-2018
How to save Singleton pattern from Reflection, Serialization and Cloning?
In Java, singleton pattern is used to avoid creation of multiple instances of a single class. After first-time object creation, if we try to do that, all will point to the same instance. In this blog, we focus not on how to create singleton class rather than we focus on those concepts by which this singleton pattern can be the break and their prevention techniques. So let's start one by one.
1. Reflection: In Java, Reflection API is used to explore or change the behavior of methods, interfaces, classes at runtime.
Let's see how reflection can break the singleton pattern:-
import java.lang.reflect.Constructor; //Singleton class class Singleton { public static Singleton singleton; private Singleton() { } public static Singleton getInstance(){ If(singleton == null){ singleton = new Singleton(); } return singleton; } } public class ReflectionDemo { public static void main(String[] args) { Singleton object1 = Singleton.singleton; Singleton object2 = null; try { Constructor[] constructors = Singleton.class.getDeclaredConstructors(); for (Constructor constructor : constructors) { // Below code will destroy the singleton pattern constructor.setAccessible(true); object2 = (Singleton) constructor.newInstance(); break; } }catch (Exception e) { e.printStackTrace(); } System.out.println("object1 .hashCode():- " + object1.hashCode()); System.out.println("object2.hashCode():- " + object2.hashCode()); } }
Output:
object1.hashCode():- 636712542
object2.hashCode():- 122163700
Explanation:-
As you can see that 2 different hashcodes are created for singleton class. Hence singleton pattern has been destroyed using Reflection.
Solution 1:
To overcome above issue we can throw a runtime exception in private constructor if singleton class is already initialized.
Check below piece of code, inside Singleton class:-
class Singleton { public static Singleton singleton; private Singleton() { if(singleton != null){ throw new RuntimeException("Do not try to break the Singleton class"); } } public static Singleton getInstance(){ If(singleton == null){ singleton = new Singleton(); } return singleton; } }
Solution 2:
To overcome above issue we can use enum also, as java internally instantiated enum value only once. The main drawback of enum is that it doesn’t support lazy initialization.
//Java program for Enum type singleton public enum Singleton { INSTANCE; }
2. Serialization: In Java, using serialization we convert an object into the byte stream and send it to the network or save in a file. And at the time de-serialize that object it will create a new object. So serialization on Singleton class is not a good idea, as it breaks the singleton property.
Let's see how Serialization can break the singleton pattern:-
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.Serializable; class Singleton implements Serializable { public static Singleton singleton; private Singleton() { } public static Singleton getInstance(){ If(singleton == null){ singleton = new Singleton(); } return singleton; } } public class SingletonRunner { public static void main(String[] args) { try { Singleton object1 = Singleton.singleton; ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file.text")); out.writeObject(object1); out.close(); // deserailize from file to object ObjectInput in = new ObjectInputStream(new FileInputStream("file.text")); Singleton object2 = (Singleton) in.readObject(); in.close(); System.out.println("object1 hashCode:- " + object1.hashCode()); System.out.println("object2 hashCode:- " + object2.hashCode()); } catch (Exception e) { e.printStackTrace(); } } }
Output:
object1 hashCode:- 2520089362
object2 hashCode:- 462123831
Explanation:
As you can see that 2 different hashcodes are created for singleton class. Hence singleton pattern has been destroyed using De-serialization.
Solution 1:
To overcome above issue we have to implement readResolve() method in Singleton class. We can either throw runtime exception from readResolve() method or either return instance of Singleton class. We can check how to implement readReolve() method:-
class Singleton implements Serializable { public static Singleton singleton; private Singleton() { } public static Singleton getInstance(){ If(singleton == null){ singleton = new Singleton(); } return singleton; } protected Object readResolve() { return singleton; } }
Solution 2:
To overcome above issue we can use enum also, as this guarantees singleton-ness of the object and return the same instance always.
//Java program for Enum type singleton public enum Singleton { INSTANCE; }
3. Cloning: In Java, object duplication or copy is created using clone() method by implementing the clonable interface. If we use cloning on singleton class, then it has two instances of the same class which breaks the singleton pattern.
Let's see how Cloning can break the singleton pattern:-
class SuperClass implements Cloneable { int i = 5; @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } // Singleton class class Singleton extends SuperClass { public static Singleton singleton; private Singleton() { } public static Singleton getInstance(){ If(singleton == null){ singleton = new Singleton(); } return singleton; } } public class SingletonRunner { public static void main(String[] args) throws CloneNotSupportedException { Singleton object1 = Singleton.singleton; Singleton object2 = (Singleton) object1.clone(); System.out.println("object1 hashCode:- " + object1.hashCode()); System.out.println("object2 hashCode:- " + object2.hashCode()); } }
Output:
object1 hashCode:- 1527082362
object2 hashCode:- 462623031
Explanation:
As you can see that 2 different hashcodes are created for singleton class. Hence singleton pattern has been destroyed using Cloning.
Solution:
To overcome above issue we have to override clone() method in Singleton class. We can either throw an exception from clone() method or either return instance of Singleton class. We can check how to implement clone() method:-
// Singleton class class Singleton extends SuperClass { public static Singleton singleton; private Singleton() { } public static Singleton getInstance(){ If(singleton == null){ singleton = new Singleton(); } return singleton; } @Override protected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); //Or we can return instance of singleton class /* return singleton; */ } }
Cookies are important to the proper functioning of a site. To improve your experience, we use cookies to remember log-in details and provide secure log-in, collect statistics to optimize site functionality, and deliver content tailored to your interests. Click Agree and Proceed to accept cookies and go directly to the site or click on View Cookie Settings to see detailed descriptions of the types of cookies and choose whether to accept certain cookies while on the site.
About Author
Navin Purohit
Navin has an experience of 4 years in Java and NodeJS. He is eager to learn new technologies and want to explore his inner strengths.