Next, we will introduce several ways to implement Singleton:
First: double check lock [not recommended]
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; } }
Starved man mode: no security problem, because the object has been created when the class is loaded
class DanLi { private DanLi(){} // Privatizing construction methods so that objects cannot be created externally private static DanLi dl = new DanLi(); // Create the object in advance, and the object will be created when the class is loaded }
Enhanced version:
class DanLi { private DanLi(){} // Privatizing construction methods so that objects cannot be created externally private static DanLi dl = new DanLi(); // Create the object in advance, and the object will be created when the class is loaded public static DanLi getDanLi(){ // Return object directly when method is called return dl; } }
Variant: instantiate INSTANCE during class initialization
public class Singleton{ private static Singleton instance = null; private Singleton(){}; static { instance = new Singleton(); } public static Singleton getInstance() { return instance; } }
Static inner class
public class Singleton{ private static class SingletonHolder { private static final Singleton instance = new Singleton(); } private Singleton() {}; public static Singleton getInstance() { return SingletonHolder.instance; } }
However, there is a common problem in the above methods: they can use AccessibleObject.setAccessible method to call private constructor to create new instance through reflection mechanism
In general, we can't operate on the private fields of a class, and reflection is no exception. At this time, we can call AccessibleObject.setAccessible(boolean flag) method to allow such access
if (s1 == s2) { // Description creates an instance } else { // Note: two different instances are created. If you need to resist this attack, you can throw an exception when you are asked to create a second instance }
It can be modified as follows:
public class ElvisModified { private static boolean flag = false; // Add logo private ElvisModified(){ synchronized(ElvisModified.class) { if(flag == false) // Identification mark { flag = !flag; } else { throw new RuntimeException("Single mode violated!"); } } } private static class SingletonHolder{ private static final ElvisModified INSTANCE = new ElvisModified(); } public static ElvisModified getInstance() { return SingletonHolder.INSTANCE; } public void doSomethingElse() { } }
After JDK 1.5, Singleton can be implemented using enumeration types of single elements
public enum Test { INSTANCE; public void dosomething() { System.out.println(this + " is speaking!"); } } // test public class TestSingleton { public static void main(String[] args) { Test t1 = Test.INSTANCE; t1.dosomething(); Test t2 = Test.INSTANCE; t2.dosomething(); System.out.println(t1 == t2); } } // result INSTANCE is speaking! INSTANCE is speaking! true
When Singleton is implemented with a single element enumeration type, an exception is thrown when a new instance is created through the reflection mechanism. Compared with the previous s1 == s2, it can be seen that the enumeration type using single element is similar to the public domain method in function, but it is more concise and provides a serialization mechanism free of charge to absolutely prevent multiple instantiations.