This article is participating in “Java Theme Month – Java Debug Notes Event”, see < Event link > for more details.


Several ways of writing singletons

I have listed 8 ways to write singletons in previous articles. See 8 ways to write singletons

In development, you probably use the lazy, double-checked script the most

public class Singleton {

    private volatile static Singleton instance;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
Copy the code

This is thread-safe, lazy lazy loading, efficient, and volatile to prevent reordering when new Singleton() creates objects.

Does this seemingly perfect writing guarantee that the Singleton object acquired multiple times is the same? Let’s try it out

Call the singleton normally to get the object

public static void main(String[] args) {
    Singleton singleton1 = Singleton.getInstance();
    Singleton singleton2 = Singleton.getInstance();
    System.out.println(singleton1);
    System.out.println(singleton2);
}
Copy the code

In a normal call, the output singleton1 and singleton2 are indeed the same object

com.test.Singleton@14514713
com.test.Singleton@14514713

Process finished with exit code 0
Copy the code

In this Singleton pattern, because the private no-argument constructor is set, we cannot break the structure of the Singleton by creating an object directly with new Singleton()

Is there a way to ignore private and call new Singleton() directly? Of course there is

A brief description of the apis commonly used in Java reflection

  • 1. Obtain the Class object

    • 1.1. Pass package name + class name

      Class<? > aClass = Class.forName(“com.test.Singleton”);

    • 1.2. Pass the class name. Class

      Class<? > aClass = Singleton.class;

    • 1.3. Use getClass ()

      Class<? > aClass = new Singleton().getClass();

  • 2. Create a class object

    • 2.1. Only the default no-argument constructor can be used

      Singleton singleton = (Singleton) clazz.newInstance();

    • 2.2. You can select a specific constructor; getConstructor() requires an argument

      Constructor<? > con = clazz.getConstructor();

      Singleton singleton = (Singleton)con.newInstance();

  • 3. Get the attributes of the class

    • 3.1. Get public

      Field[] fields = clazz.getFields();

    • 3.2. Get all, including private

      Field[] declaredFields = clazz.getDeclaredFields();

  • 4. Get the method of the class

    • 4.1. Get all methods in a class, excluding private

      Method[] methods = clazz.getMethods();

    • 4.1. Get the private method in the class

      Method[] declaredMethods = clazz.getDeclaredMethods();

  • 5. Get the constructor for the class

    • 5.1. Get no-parameter constructors, including private

      Constructor<? > declaredConstructor = clazz.getDeclaredConstructor();

    • 5.2. Get the public constructor

      Constructor<? >[] constructors = clazz.getConstructors();

    • 5.3. Get all constructors, including private

      Constructor<? >[] declaredConstructors = clazz.getDeclaredConstructors();

Use reflection call singletons to get objects

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1); System.out.println(singleton2); Class<? > clazz = Singleton.class; // Use reflection to get Constructor<? > constructor = clazz.getDeclaredConstructor(); / / will be Accessible set to true, you can ignore the private access to private methods or the value of a variable, not set throws an exception constructor. SetAccessible (true); System.out.println(constructor.newInstance()); }Copy the code

Note that if you call a private method through reflection, you need to set Accessible to true, otherwise an exception will be executed

The execution result

com.test.Singleton@14514713
com.test.Singleton@14514713
com.test.Singleton@69663380

Process finished with exit code 0
Copy the code

As you can see from the output, this is not the same object. Because we broke the Singleton structure with reflection, we could go directly to New Singleton().

So what kind of singleton can ignore reflection? That’s enumeration

Enumerate singleton patterns

Why is enumeration the best way to do it, and you can look at the last of the 8 ways to write the singleton pattern in the previous article

More detailed enumeration singleton pattern writing

Public class EnumSingleton {private EnumSingleton() {} /** * public static ** @return EnumSingleton */ EnumSingleton getInstance() { return ContainerHolder.HOLDER.instance; } private enum ContainerHolder { HOLDER; private EnumSingleton instance; // ContainerHolder() {instance = new enumleton (); }}}Copy the code

Test whether reflection can break this singleton

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException {/ / normal call EnumSingleton singleton1 = EnumSingleton. GetInstance (); EnumSingleton singleton2 = EnumSingleton.getInstance(); System.out.println(singleton1); System.out.println(singleton2); // Call EnumSingleton Class<? > clazz = EnumSingleton.class; Constructor<? > constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); EnumSingleton enumSingleton = (EnumSingleton) constructor.newInstance(); System.out.println(enumSingleton.getInstance()); // Reflection calls the inner enumeration ContainerHolder Class<? > containerHolder = Class.forName("com.wupao.channel.EnumSingleton$ContainerHolder"); / / complains here, Java. Lang. NoSuchMethodException Constructor <? > declaredConstructor = containerHolder.getDeclaredConstructor(); declaredConstructor.setAccessible(true); / / reflection calls System. Out. Println (declaredConstructor. NewInstance ()); }Copy the code

The output

com.test.EnumSingleton@14514713
com.test.EnumSingleton@14514713
com.test.EnumSingleton@14514713
Exception in thread "main" java.lang.NoSuchMethodException: com.test.EnumSingleton$ContainerHolder.<init>()
	at java.lang.Class.getConstructor0(Class.java:3082)
	at java.lang.Class.getDeclaredConstructor(Class.java:2178)
	at com.test.Test.main(Test.java:60)

Process finished with exit code 1
Copy the code

The third output is the same object as the first two

Error NoSuchMethodException NoSuchMethodException NoSuchMethodException NoSuchMethodException NoSuchMethodException

Looking at the Enum source code (java.lang.enum), you see that Enum has two parameterized constructors

Change the code and pass in two arguments

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, ClassNotFoundException {// Reflection calls internal enumeration ContainerHolder Class<? > containerHolder = Class.forName("com.wupao.channel.EnumSingleton$ContainerHolder"); // Pass two arguments Constructor<? > declaredConstructor = containerHolder.getDeclaredConstructor(String.class, int.class); declaredConstructor.setAccessible(true); / / reflection calls System. Out. Println (declaredConstructor. NewInstance ()); }Copy the code

The execution result

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.test.Test.main(Test.java:63)

Process finished with exit code 1
Copy the code

Cannot reflectively create enum objects Cannot create enum objects by reflection.

Therefore, enumerating this way, is able to prevent reflection to do damage!