為什麼java中用列舉實現單例模式會更好
程式碼簡潔
這是迄今為止最大的優點,如果你曾經在Java5之前寫過單例模式程式碼,那麼你會知道即使是使用雙檢鎖你有時候也會返回不止一個例項物件。雖然這種問題通過改善java記憶體模型和使用volatile變數可以解決,但是這種方法對於很多初學者來說寫起來還是很棘手。相比用 synchronization的雙檢鎖實現方式來說,列舉單例就簡單多了。你不相信?比較一下下面的雙檢鎖實現程式碼和列舉實現程式碼就知道了。
用列舉實現的單例:
這是我們通常寫列舉單例的方式,它可能包含例項變數和例項方法,但是簡單來說我什麼都沒用,需要注意的是如果你使用例項方法,你就需要確保方法的執行緒安全性,避免它會影響物件的狀態。通常情況下列舉裡面建立例項是執行緒安全的,但是其它的方法就需要程式設計者自己去考慮了。
public enum EasySingleton{
INSTANCE;
}
程式碼就這麼簡單,你可以使用EasySingleton.INSTANCE呼叫它,比起你在單例中呼叫getInstance()方法容易多了。
用雙檢索實現單例:
下面的程式碼是用雙檢索實現單例模式的例子,在這裡getInstance()方法檢查了兩次來判斷INSTANCE是否為null,這就是為什麼叫雙檢索的原因,記住雙檢索在java5之前是有問題的,但是java5在記憶體模型中有了volatile變數之後就沒問題了。
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()來獲得例項物件。
現在看看二者建立一個懶載入執行緒安全的單例需要的程式碼數量。
使用列舉單例模式你只需要一行程式碼搞定因為列舉例項的建立是執行緒安全的。
你可能會說比起使用雙檢索方法還有更好的方法實現單例模式,但是任何一種方法都有它的利和弊,就像我下面例子中展示的我很喜歡的一種在類載入期間初始化靜態域的單例實現方式,但是要記住這不是一種懶載入單例方式。
用靜態工廠方法實現單例:
這是java中我比較喜歡的一種實現單例模式的方法,由於單例例項是static和final的,當類第一次被載入到記憶體它就例項化了,所以這種例項的建立方式是執行緒安全的。
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.getInstance()方法來獲得例項物件。
2)列舉單例可以自己處理序列化
傳統的單例模式的另外一個問題是一旦你實現了serializable介面,他們就不再是單例的了,因為readObject()方法總是返回一個 新的例項物件,就像java中的構造器一樣。你可以使用readResolve()方法來避免這種情況,通過像下面的例子中這樣用單例來替換新建立的實 例:
private Object readResolve(){
return INSTANCE;
}
如果你的單例類包含狀態的話就變的更復雜了,你需要把他們置為transient狀態,但是用列舉單例的話,序列化就不要考慮了。
3)列舉單例是執行緒安全的
就像第一點提到的,由於列舉例項的建立預設就是執行緒安全的,你不需要擔心雙檢鎖問題。
總結:通過提供序列化和執行緒安全並且幾行程式碼搞定,說明列舉單例模式是java5之後建立單例最好的方法。你仍然可以使用其它你感覺很流行的方式來建立單例,但是我還是要找一個能夠使我信服的觀點讓我不去使用列舉作為單例,如果你有,請告訴我!
相關文章
- Java 利用列舉實現單例模式Java單例模式
- Java列舉:為什麼它是單例模式的最佳選擇?Java單例模式
- 為什麼我牆裂建議大家使用列舉來實現單例。單例
- 為什麼要用單例模式?單例模式
- 你真的會寫單例模式嗎——Java實現單例模式Java
- Spring使用實現類注入為什麼會導致高耦合度(舉例)Spring
- 單例模式的 Java 實現與思考單例模式Java
- 五種方式實現 Java 單例模式Java單例模式
- 設計模式:單例模式的使用和實現(JAVA)設計模式單例Java
- Java 實現單例模式的 9 種方法Java單例模式
- 單例模式的各種實現方式(Java)單例模式Java
- Effective Java - 構造器私有、列舉和單例Java單例
- 為什麼說列舉更佔記憶體,列舉原理是什麼?記憶體
- Qt 中用Q_GLOBAL_STATIC來實現執行緒安全的單例模式QT執行緒單例模式
- PHP實現單例模式PHP單例模式
- golang實現單例模式Golang單例模式
- Rust實現單例模式Rust單例模式
- Java設計模式——實現單例模式的七種方式[JZOF]Java設計模式單例
- 美團一面:會單例模式嗎,寫個單例看看?(8大單例模式實現方式總結)單例模式
- 為什麼建議你使用列舉?
- 單例模式實現對比單例模式
- 單例模式c++實現單例模式C++
- Python中實現單例模式Python單例模式
- JAVA中實現單例(Singleton)模式的八種方式Java單例模式
- java 單例模式Java單例模式
- Java單例模式Java單例模式
- 單例模式 – 單例登錄檔與 Spring 實現單例剖析單例模式Spring
- 為什麼使用列舉作為配置項(enum as configuration)是反開發模式的模式
- DCL單例模式中的缺陷及單例模式的其他實現單例模式
- JS中的單例模式及單例模式原型類的實現JS單例模式原型
- 舉例說明BFC會與float元素相互覆蓋嗎?為什麼?
- 使用列舉來寫出更優雅的單例設計模式單例設計模式
- 【php實現設計模式】之單例模式PHP設計模式單例
- 用Python實現設計模式——單例模式Python設計模式單例
- 利用static來實現單例模式單例模式
- 單例模式:5種實現方式單例模式
- 單例模式的各種實現單例模式
- Java設計模式【單例模式】Java設計模式單例
- Java設計模式 | 單例模式Java設計模式單例