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
Share

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *