Singleton pattern - breaking Singleton
Table of Contents
Breaking Singleton
Breaking Singleton using Reflection
import java.lang.reflect.Constructor;
public class TestDriver {
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = null;
try {
Constructor[] constructors = Singleton.class.getDeclaredConstructors();
for (Constructor constructor: constructors) {
constructor.setAccessible(true);
singleton2 = (Singleton) constructor.newInstance();
break;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("instance1.hashCode(): " + singleton1.hashCode());
System.out.println("instance2.hashCode(): " + singleton2.hashCode());
// Output
// instance1.hashCode(): 1159190947
// instance2.hashCode(): 804564176
}
}
Enum Singleton
To overcome this situation with Reflection, we can use the Enum to implement Singleton design pattern. Java ensures any enum value is instantiated only once in a Java program.
public enum EnumSingleton {
INSTANCE;
public static void work() {
//do something
}
}
Breaking Singleton using Serialization
In certain case, you may be forced to implement Serializable interface in Singleton class, especially in the distributed systems. Whenever we de-serialize a class, it creates a new instance of the class. This can be challenging, especially with Singleton pattern.
import java.io.Serializable;
public class SingletonWithSerialized implements Serializable {
private static final long serialVersionUID = 1L;
private SingletonWithSerialized() {}
private static SingletonWithSerialized instance = new SingletonWithSerialized();
public static SingletonWithSerialized getInstance() {
return SingletonWithSerialized.instance;
}
}
You can prevent this by implementing the readResolve()
method.
protected Object readResolve() {
return getInstance();
}