爲何java中用枚舉實現單例模式會更好

       枚舉單例是java中使用枚舉提供一個實例對象來實現單例模式的一種新方法,雖然單例模式在java中早已存在,但枚舉單例實際上從java5引入枚舉做爲它的關鍵特性以後相對來講仍是一個新的概念,這篇文章和我先前發表的關於單例的文章有必定的關聯性,一篇是 java單例模式的10個面試問題,講的是java面試中關於單例模式常見的10個問題,另外一篇是 10個關於java枚舉的例子,這裏面講了枚舉的不少做用。這篇文章是關於咱們爲何要使用枚舉來實現單例模式,它和傳統的單例模式實現方法比起來有哪些好處呢?

      下面是我總結的使用枚舉實現單例模式的幾個緣由。另外,若是你喜歡關於設計模式的文章,你也能夠看看我發表的關於生成器模式裝飾器模式的文章。 html

1)枚舉單例模式代碼簡潔 java

      這是迄今爲止最大的優勢,若是你曾經在java5以前寫過單例模式實現代碼,那麼你會知道即便是使用雙檢鎖你有時候也會返回不止一個實例對象。雖然這種問 題經過改善java內存模型和使用volatile變量能夠解決,可是這種方法對於不少初學者來講寫起來仍是很棘手。相比用 synchronization的雙檢索實現方式來講,枚舉單例就簡單多了。你不相信?比較一下下面的雙檢索實現代碼和枚舉實現代碼就知道了。 面試

用枚舉實現的單例: 編程

這是咱們一般寫枚舉單例的方式,它可能包含實例變量和實例方法,可是簡單來講我什麼都沒用,須要注意的是若是你使用實例方法,你就須要確保方法的線程安全性,避免它會影響對象的狀態。一般狀況下枚舉裏面建立實例是線程安全的,可是其它的方法就須要編程者本身去考慮了。 設計模式

/** 安全

* Singleton pattern example using Java Enumj ui

*/ spa

public enum EasySingleton{
    INSTANCE;
}
線程

代碼就這麼簡單,你可使用EasySingleton.INSTANCE調用它,比起你在單例中調用getInstance()方法容易多了。 設計

用雙檢索實現單例:

下面的代碼是用雙檢索實現單例模式的例子,在這裏getInstance()方法檢查了兩次來判斷INSTANCE是否爲null,這就是爲何叫雙檢索的緣由,記住雙檢索在java5以前是有問題的,可是java5在內存模型中有了volatile變量以後就沒問題了。

/**

* 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()來得到實例對象。

如今看看二者建立一個懶加載線程安全的單例須要的代碼數量。使用枚舉單例模式你只須要一行代碼搞定由於枚舉實例的建立是線程安全的。

你可能會說比起使用雙檢索方法還有更好的方法實現單例模式,可是任何一種方法都有它的利和弊,就像我下面例子中展現的我很喜歡的一種在類加載期間初始化靜態域的單例實現方式,可是要記住這不是一種懶加載單例方式。

用靜態工廠方法實現單例:

這是java中我比較喜歡的一種實現單例模式的方法,因爲單例實例是static和final的,當類第一次被加載到內存它就實例化了,因此這種實例的建立方式是線程安全的。

/**

* 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.getInstance()方法來得到實例對象。

2)枚舉單例能夠本身處理序列化

傳統的單例模式的另一個問題是一旦你實現了serializable接口,他們就再也不是單例的了,由於readObject()方法老是返回一個 新的實例對象,就像java中的構造器同樣。你可使用readResolve()方法來避免這種狀況,經過像下面的例子中這樣用單例來替換新建立的實 例:

//readResolve to prevent another instance of Singleton
    
private Object readResolve(){
        
return INSTANCE;
    
}

若是你的單例類包含狀態的話就變的更復雜了,你須要把他們置爲transient狀態,可是用枚舉單例的話,序列化就不要考慮了。

3)枚舉單例是線程安全的

就像第一點提到的,因爲枚舉實例的建立默認就是線程安全的,你不須要擔憂雙檢鎖問題。

 

總結:經過提供序列化和線程安全而且幾行代碼搞定,說明枚舉單例模式是java5以後建立單例最好的方法。你仍然可使用其它你感受很流行的方式來建立單例,可是我仍是要找一個可以使我信服的觀點讓我不去使用枚舉做爲單例,若是你有,請告訴我!(*^__^*)

相關文章
相關標籤/搜索