【單例設計模式】單例模式中爲何用枚舉更好

枚舉單例(Enum Singleton)是實現單例模式的一種新方式,儘管單例模式在java中已經存在很長時間了,可是枚舉單例相對來講是一種比較新的概念,枚舉這個特性是在Java5纔出現的,這篇文章主要講解關於爲何咱們應該使用枚舉來實現單例模式,它與傳統方式實現的單例模式相比較又有哪些優點?html

1. 枚舉寫法簡單java

寫法簡單這是它最大的優勢,若是你先前寫過單例模式,你應該知道即便有DCL(double checked locking) 也可能會建立不止一個實例,儘管在Java5這個問題修復了(jdk1.5在內存模型上作了大量的改善,提供了volatile關鍵字來修飾變量),可是仍然對新手來講仍是比較棘手。對比經過double checked locking 實現同步,枚舉單例那實在是太簡單了。若是你不相信那麼對比下面代碼,分別爲傳統的用double checked locking實現的單例和枚舉單例。程序員

枚舉實現:安全

下面這段代碼就是聲明枚舉實例的一般作法,它可能還包含實例變量和實例方法,可是爲了簡單起見,我並無使用這些東西,僅僅須要當心的是若是你正在使用實例方法,那麼你須要確保線程安全(若是它影響到其餘對象的狀態的話)。默認枚舉實例的建立是線程安全的,可是在枚舉中的其餘任何方法由程序員本身負責。線程

/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
    INSTANCE;
}

你能夠經過EasySingleton.INSTANCE來訪問,這比調用getInstance()方法簡單多了。htm

double checked locking 實現法:對象

下面代碼就是用double checked locking 方法實現的單例,這裏的getInstance()方法要檢查兩次,確保是否實例INSTANCE是否爲null或者已經實例化了,這也是爲何叫double checked locking 模式。接口

/**
* 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的。get

人們可能會爭論有更好的方式去寫單例用來替換duoble checked locking 方法,可是每種方法有他本身的優勢和缺點,象我不少時候更願初始化經過類加載靜態字段,以下所示,可是記住他不是lazy loaded形式的單例。

靜態工廠實現法:

這是我最喜歡的一種方式來實現單例模式,由於單例是靜態的final變量,當類第一次加載到內存中的時候就初始化了,因此建立的實例當然是thread-safe。

/**
* 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()方法來避免此事發生,看下面的例子:

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

這樣甚至還能夠更復雜,若是你的單例類維持了其餘對象的狀態的話,所以你須要使他們成爲transient的對象。可是枚舉單例,JVM對序列化有保證。

3. 枚舉實例建立是thread-safe

正如在第一條中所說的,由於建立枚舉默認就是線程安全的,你不須要擔憂double checked locking。

總結:枚舉單例有序列化和線程安全的保證,並且只要幾行代碼就能實現是單例最好的的實現方式,不過你仍然可使用其它的方式來實現單例,可是我仍然得不到一個更有信服力的緣由不去使用枚舉。若是你有的話,不妨告訴我。

原網頁地址:http://www.importnew.com/6461.html

相關文章
相關標籤/搜索