【單例深思】枚舉實現單例原理

單例的枚舉實如今《Effective Java》中有提到,由於其功能完整、使用簡潔、無償地提供了序列化機制、在面對複雜的序列化或者反射攻擊時仍然能夠絕對防止屢次實例化等優勢,單元素的枚舉類型被做者認爲是實現Singleton的最佳方法。java


其實現很是簡單,以下:

public enum Singleton {
    INSTANCE;
    private Singleton() {}
}
 
下面咱們用一個枚舉實現單個數據源例子來簡單驗證一下:
聲明一個枚舉,用於獲取數據庫鏈接。
 
public enum DataSourceEnum {
    DATASOURCE;
    private DBConnection connection = null;
 
    private DataSourceEnum() {
        connection = new DBConnection();
    }
 
    public DBConnection getConnection() {
        return connection;
    }
}  
 
模擬一個數據庫鏈接類:
 
public class DBConnection {} 
 
測試經過枚舉獲取的實例是否相同:
 
 public class Main {
 
    public static void main(String[] args) {
        DBConnection con1 = DataSourceEnum.DATASOURCE.getConnection();
        DBConnection con2 = DataSourceEnum.DATASOURCE.getConnection();
        System.out.println(con1 == con2);
    }
}

輸出結果爲:true  結果代表兩次獲取返回了相同的實例。
 
下面深刻了解一下爲何枚舉會知足線程安全、序列化等標準。
 
在JDK5 中提供了大量的語法糖,枚舉就是其中一種。
所謂 語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家 Peter.J.Landin 發明的一個術語,指在計算機語言中添加的某種語法,這種語法對語言的功能並無影響,可是可是更方便程序員使用。只是在編譯器上作了手腳,卻沒有提供對應的指令集來處理它。
 
就拿枚舉來講,其實Enum就是一個普通的類,它繼承自java.lang.Enum類。
 
public enum DataSourceEnum {
    DATASOURCE;
 
}  
 
把上面枚舉編譯後的字節碼反編譯,獲得的代碼以下:
 
public final class DataSourceEnum extends Enum<DataSourceEnum> {
      public static final DataSourceEnum DATASOURCE;
      public static DataSourceEnum[] values();
      public static DataSourceEnum valueOf(String s);
      static {};
}
 
由反編譯後的代碼可知,DATASOURCE 被聲明爲 static 的,根據在【單例深思】餓漢式與類加載 中所描述的類加載過程,能夠知道虛擬機會保證一個類的<clinit>() 方法在多線程環境中被正確的加鎖、同步。因此,枚舉實現是在實例化時是線程安全。
 
接下來看看序列化問題:
 
Java規範中規定,每個枚舉類型極其定義的枚舉變量在JVM中都是惟一的,所以在枚舉類型的序列化和反序列化上,Java作了特殊的規定。
在序列化的時候Java僅僅是將枚舉對象的name屬性輸出到結果中,反序列化的時候則是經過 java.lang.Enum 的 valueOf() 方法來根據名字查找枚舉對象。
也就是說,如下面枚舉爲例,序列化的時候只將 DATASOURCE 這個名稱輸出,反序列化的時候再經過這個名稱,查找對於的枚舉類型,所以反序列化後的實例也會和以前被序列化的對象實例相同。
 
public enum DataSourceEnum {
    DATASOURCE;
 
}  
 
由此可知,枚舉天生保證序列化單例。
相關文章
相關標籤/搜索