Java讀書筆記(8)-單例模式

      今天在閱讀《Effective Java 2》第3條時,獲知一種使用枚舉enum實現單例模式的新方法,然而書上並無就此展開深刻說明,因而上網查閱了一些資料,現將收集的資料整理於下:算法

      轉自:枚舉類型的單例模式:http://callmegod.iteye.com/blog/1474441編程

Singleton 模式是在編程實踐中應用最普遍的幾種設計模式之一。之前知道的,實現單例的方法有兩種(下面的A、B)。剛剛在讀《Effective Java的時候》學到一種新的更好的方法(E):單元素的枚舉類型。同時經過網上資料也知道了其餘兩種方法(C、D)。最後一種在Java中從1.5版本 開始支持,其餘語言在驗證後說明。設計模式

A.餓漢式(類加載的時候就建立實例)。
代碼以下:安全

public class MaYun {
public static final Mayun instance = new Mayun(); //靜態的final的MaYun
private MaYun() {
//MaYun誕生要作的事情
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
}
Call:MaYun.instance.splitAlipay();多線程

Feature:能夠經過反射機制攻擊;線程安全[多個類加載器除外]。this

A+.餓漢變種[推薦]spa

public class MaYun {
private static Mayun instance = new Mayun();
private static getInstance() {
return instance;
}
private MaYun() {
//MaYun誕生要作的事情
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
}線程

A++.餓漢變種(類初始化的時候實例化instance):設計

public class MaYun {
private MaYun instance = null;
static {
instance = new MaYun();
}
private MaYun() {
//MaYun誕生要作的事情
}
public static MaYun getInstance() {
return this.instance;
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
}對象

B.懶漢式。
代碼以下:

public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun誕生要作的事情
}
public static MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
}
Call:MaYun.getInstance().splitAlipay();

Feature:延時加載;線程不安全,多線程下不能正常工做;須要額外的工做(Serializable、transient、readResolve())來實現序列化。

B+.懶漢式變種。

public class MaYun {
private static MaYun instance = null;
private MaYun() {
//MaYun誕生要作的事情
}
public static synchronized MaYun getInstance() {
if (instance == null) {
instance = new MaYun();
}
return instance;
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
}

Feature:線程安全;效率比較低,由於須要線程同步的時候比較少。

C.靜態內部類[推薦]。
代碼以下:

public class MaYun {
private static class SingletonHolder {
private static final instance = new MaYun();
}
public static final getInstance() {
return SingletonHolder.instance;
}
private MaYun() {
//MaYun誕生要作的事情
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
Call:MaYun.getInstance().splitAlipay();

Feature:線程安全;延遲加載。

D.雙重校驗鎖[不推薦]。
代碼以下:

public class MaYun {
private volatile static MaYun instance;
private MaYun (){}
public static MaYun getInstance() {
if (instance == null) {
synchronized (MaYun.class) {
if (instance == null) {
instance = new MaYun();
}
}
}
return instance;
}
}

Feature:jdk1.5以後才能正常達到單例效果。

E.編寫一個包含單個元素的枚舉類型[極推薦]。
代碼以下:

public enum MaYun {
himself; //定義一個枚舉的元素,就表明MaYun的一個實例
private String anotherField;
MaYun() {
//MaYun誕生要作的事情
//這個方法也能夠去掉。將構造時候須要作的事情放在instance賦值的時候:
/** himself = MaYun() {
* //MaYun誕生要作的事情
* }
**/
}
public void splitAlipay() {
System.out.println(「Alipay是個人啦!看你丫Yahoo綠眉綠眼的望着。。。」);
}
}
Call:MaYun.himself.splitAlipay();

Feature:從Java1.5開始支持;無償提供序列化機制,絕對防止屢次實例化,即便在面對複雜的序列化或者反射攻擊的時候。

總之,五類:懶漢,餓漢,雙重校驗鎖,靜態內部類,枚舉。
餓漢:由於加載類的時候就建立實例,因此線程安全(多個ClassLoader存在時例外)。缺點是不能延時加載。
懶漢:須要加鎖才能實現多線程同步,可是效率會下降。優勢是延時加載。
雙重校驗鎖:麻煩,在當前Java內存模型中不必定都管用,某些平臺和編譯器甚至是錯誤的,由於instance = new MaYun()這種代碼在不一樣編譯器上的行爲和實現方式不可預知。
靜態內部類:延遲加載,減小內存開銷。由於用到的時候才加載,避免了靜態field在單例類加載時即進入到堆內存的permanent代而永遠得不到回收的缺點(大多數垃圾回收算法是這樣)。
枚舉:很好,不只能避免多線程同步問題,並且還能防止反序列化從新建立新的對象。可是失去了類的一些特性,沒有延遲加載,用的人也太少了~~

    比較了這麼多中單例實現模式,發現使用枚舉enum除了沒有延遲加載這一點好處以外,不管是編程方便程度和安全性都是最後的,之後嘗試在本身開發中使用這種新的方法。

相關文章
相關標籤/搜索