Singleton Invalidated by Reflection
Using reflection we can set the private constructor to become accessible at runtime as shown in the example below.
Example
package com.loopandbreak.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class SingletonInvalidatedByReflection { private static SingletonInvalidatedByReflection instance = new SingletonInvalidatedByReflection(); private SingletonInvalidatedByReflection() { System.out.println("Creating instance"); } public static SingletonInvalidatedByReflection getInstance() { return instance; } public static void main(String[] args) throws Exception { SingletonInvalidatedByReflection singleton1 = SingletonInvalidatedByReflection.getInstance(); SingletonInvalidatedByReflection singleton2 = SingletonInvalidatedByReflection.getInstance(); print(singleton1); print(singleton2); //Breaking the singleton design pattern using reflection Class class1 = Class.forName("com.loopandbreak.singleton.SingletonInvalidatedByReflection"); Constructor<SingletonInvalidatedByReflection> constructor = class1.getDeclaredConstructor(); constructor.setAccessible(true); SingletonInvalidatedByReflection s1 = constructor.newInstance(); print(s1); } private static void print(SingletonInvalidatedByReflection singleton) { System.out.println(singleton); } }
Output
Creating instance 460141958 460141958 Creating instance 1163157884
In above output we are seeing first two objects are assigned same hashcode, because they are one object, but in 3rd call a new hashcode is returned, which means that a new object has been created after opening up constructor with reflection.
How to fix: Throw Runtime Exception if someone tries to make instance in case one instance already exists. This code will go into the private constructor of the Singleton class.
Solution
package com.loopandbreak.singleton; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class SingletonInvalidatedByReflection { private static SingletonInvalidatedByReflection instance = new SingletonInvalidatedByReflection(); private SingletonInvalidatedByReflection() { // Fix to avoid singleton using reflection if(instance != null) throw new RuntimeException("Instance already exists"); System.out.println("Creating instance"); } public static SingletonInvalidatedByReflection getInstance() { return instance; } public static void main(String[] args) throws Exception { SingletonInvalidatedByReflection singleton1 = SingletonInvalidatedByReflection.getInstance(); SingletonInvalidatedByReflection singleton2 = SingletonInvalidatedByReflection.getInstance(); print(singleton1); print(singleton2); //Breaking the singleton design pattern using reflection Class class1 = Class.forName("com.loopandbreak.singleton.SingletonInvalidatedByReflection"); Constructor<SingletonInvalidatedByReflection> constructor = class1.getDeclaredConstructor(); constructor.setAccessible(true); SingletonInvalidatedByReflection s1 = constructor.newInstance(); print(s1); } private static void print(SingletonInvalidatedByReflection singleton) { System.out.println(singleton.hashCode()); } }
Output
Creating instance 460141958 460141958 Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:422) at com.loopandbreak.singleton.SingletonInvalidatedByReflection.main(SingletonInvalidatedByReflection.java:33) Caused by: java.lang.RuntimeException: Instance already exists at com.loopandbreak.singleton.SingletonInvalidatedByReflection.<init>(SingletonInvalidatedByReflection.java:13) ... 5 more Process finished with exit code 1