Item 3: Enforce the singleton property with a private constructor or an enum type
A singleton is simply a class that is instantiated exactly once
- Before 1.5: There were two ways to implement singletons.
- 1.the member is a final field: (simpler)
- [-] a privileged client can invoke the private constructor reflectively with the aid of the AccessibleObject.setAccessible method.
-
to defend against this attack, modify the constructor to make it throw an exception if it’s asked to create a second instance.
// Singleton with public final field public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public void leaveTheBuilding() { ... } }
-
2.the public member is a static factory method
// Singleton with static factory public class Elvis { private static final Elvis INSTANCE = new Elvis(); private Elvis() { ... } public static Elvis getInstance() { return INSTANCE; } public void leaveTheBuilding() { ... } }
- [-] a client can create a instance using private Constructor by Reflection
- To resolve this problem
- 1.declare all instance fields transient
-
2.provide a readResolve method
// readResolve method to preserve singleton property private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; }
- To resolve this problem
- 1.the member is a final field: (simpler)
- [The Best]As of release 1.5, a single-element enum type is the best way to implement a singleton. (even in the face of sophisticated serialization or reflection attacks.)
- [+] more concise
- [+] provides the serialization machinery for free
- [+] provides an ironclad guarantee against multiple instantiation,
-
[-] Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
// Example 1 // Enum singleton - the preferred approach public enum Elvis { INSTANCE; public void leaveTheBuilding() { ... } } // Example 2 public enum Singleton { INSTANCE; private String name; public String getName(){ return name; } public void setName(String name){ this.name = name; } }
Examples:
all these examples are need extra workload.
- Serializable: (Serializable、transient、readResolve())
- Reflection: create an instance using private Constructor.
// Example 1
//create instance when first loading. [-] memory cost when loading
public class Singleton {
private static Singleton = new Singleton();
private Singleton() {}
public static getSignleton(){
return singleton;
}
}
// Example 2
//[-] non-thread safe
public class Singleton {
private static Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton() {
if(singleton == null) singleton = new Singleton();
return singleton;
}
}
// Example 3
// [-]thread safe but not efficient
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
return singleton;
}
}
/*
Example 4
Improvement: Double check
Problem: In JVM, creation and assignment are two independent operations. (instance = new Singleton();)
Example:
1. Thread A, B execute IF condition at the same time.
2. Thread A find instance is null. Then JVM will create a space in Hotspot JVM Heap, while the instace has not been initialized at the moment.
3. Thread B find there is a instance and it try to call the instance's method. (ERROR!!!)
*/
public class Singleton {
private static volatile Singleton singleton = null;
private Singleton(){}
public static Singleton getSingleton(){
if(singleton == null){
synchronized (Singleton.class){
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
}
/*
Example 5
INNER class, thread mutex (Thread-Safe)
Problem: 1. concern about (Serializable、transient、readResolve())
2. If Class Singleton's creator throws an exception. We will never get a instance.
Nothing is perfect!!!
*/
public class Singleton {
private Singleton() {
}
/* inner class is responsible for managing Singleton instance */
private static class SingletonFactory {
private static Singleton instance = new Singleton();
}
/* get instance */
public static Singleton getInstance() {
return SingletonFactory.instance;
}
public Object readResolve() {
return getInstance();
}
}
// Example 6: Two method
public class SingletonSample6 {
private static SingletonSample6 instance = null;
private SingletonSample6() {
}
private static synchronized void syncInit() {
if (instance == null) {
instance = new SingletonSample3();
}
}
public static SingletonSample6 getInstance() {
if (instance == null) {
syncInit();
}
return instance;
}
}