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!