為什么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之后創建單例最好的方法。你仍然可以使用其它你感覺很流行的方式來創建單例,但是我還是要找一個能夠使我信服的觀點讓我不去使用枚舉作為單例,如果你有,請告訴我!


推薦一個良心公眾號【IT資源社】:

本公眾號致力於免費分享全網最優秀的視頻資源,學習資料,面試經驗等,前端,PHP,JAVA,算法,Python,大數據等等,你想要的這都有

IT資源社-QQ交流群:625494093

也可添加微信拉你進微信群: super1319164238

微信搜索公眾號:ITziyuanshe 或者掃描下方二維碼直接關注,

 



作者:芥末無疆sss
鏈接:https://www.jianshu.com/p/7b3de9e0c983


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM