單例模式中為什麽用枚舉更好
枚舉單例(Enum Singleton)是實現單例模式的一種新方式,盡管單例模式在java中已經存在很長時間了,但是枚舉單例相對來說是一種比較新的概念,枚舉這個特性是在Java5才出現的,這篇文章主要講解關於為什麽我們應該使用枚舉來實現單例模式,它與傳統方式實現的單例模式相比較又有哪些優勢?
1. 枚舉寫法簡單
寫法簡單這是它最大的優點,如果你先前寫過單例模式,你應該知道即使有DCL(double checked locking) 也可能會創建不止一個實例,盡管在Java5這個問題修復了(jdk1.5在內存模型上做了大量的改善,提供了volatile關鍵字來修飾變量),但是仍然對新手來說還是比較棘手。對比通過double checked locking 實現同步,枚舉單例那實在是太簡單了。如果你不相信那麽對比下面代碼,分別為傳統的用double checked locking實現的單例和枚舉單例。
枚舉實現:
下面這段代碼就是聲明枚舉實例的通常做法,它可能還包含實例變量和實例方法,但是為了簡單起見,我並沒有使用這些東西,僅僅需要小心的是如果你正在使用實例方法,那麽你需要確保線程安全(如果它影響到其他對象的狀態的話)。默認枚舉實例的創建是線程安全的,但是在枚舉中的其他任何方法由程序員自己負責。
1 2 3 4 5 6 |
/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
INSTANCE;
}
|
你可以通過EasySingleton.INSTANCE來訪問,這比調用getInstance()方法簡單多了。
double checked locking 實現法:
下面代碼就是用double checked locking 方法實現的單例,這裏的getInstance()方法要檢查兩次,確保是否實例INSTANCE是否為null或者已經實例化了,這也是為什麽叫double checked locking 模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{ private volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public DoubleCheckedLockingSingleton getInstance(){
if (INSTANCE == null ){
synchronized (DoubleCheckedLockingSingleton. class ){
//double checking Singleton instance
if (INSTANCE == null ){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
|
你可以使用 DoubleCheckedLockingSingleton.getInstance()來獲取實例。
從創建一個lazy loaded thread-safe單例來看,它的代碼行數與枚舉相比,後者可以全部在一行內完成,因為枚舉創建的單例在JVM層面上也能保證實例是thread-safe的。
人們可能會爭論有更好的方式去寫單例用來替換duoble checked locking 方法,但是每種方法有他自己的優點和缺點,象我很多時候更願初始化通過類加載靜態字段,如下所示,但是記住他不是lazy loaded形式的單例。
靜態工廠實現法:
這是我最喜歡的一種方式來實現單例模式,因為單例是靜態的final變量,當類第一次加載到內存中的時候就初始化了,所以創建的實例固然是thread-safe。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/**
* Singleton pattern example with static factory method
*/
public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton();
//to prevent creating another instance of Singleton
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
|
你可以調用Singleton.getSingleton()獲取實例。
2. 枚舉自己處理序列化
傳統單例存在的另外一個問題是一旦你實現了序列化接口,那麽它們不再保持單例了,因為readObject()方法一直返回一個新的對象就像java的構造方法一樣,你可以通過使用readResolve()方法來避免此事發生,看下面的例子:
1 2 3 4 |
//readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
|
這樣甚至還可以更復雜,如果你的單例類維持了其他對象的狀態的話,因此你需要使他們成為transient的對象。但是枚舉單例,JVM對序列化有保證。
3. 枚舉實例創建是thread-safe
正如在第一條中所說的,因為創建枚舉默認就是線程安全的,你不需要擔心double checked locking。
總結:枚舉單例有序列化和線程安全的保證,而且只要幾行代碼就能實現是單例最好的的實現方式,不過你仍然可以使用其它的方式來實現單例,但是我仍然得不到一個更有信服力的原因不去使用枚舉。如果你有的話,不妨告訴我。
原文鏈接: Javarevisited 翻譯: ImportNew.com - 劉誌軍
譯文鏈接: http://www.importnew.com/6461.html
[ 轉載請保留原文出處、譯者和譯文鏈接。]
單例模式中為什麽用枚舉更好