枚舉類型的單例模式(java)

Inspired by Effective Java.

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:可以通過反射機制攻擊;線程安全[多個類加載器除外]。

A+.餓漢變種[推薦]

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 SigletonHolder {
private static final instance = new MaYun();
}
public static final getInstance() {
return SigletonHolder.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代而永遠得不到回收的缺點(大多數垃圾回收算法是這樣)。
枚舉:很好,不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象。但是失去了類的一些特性,沒有延遲加載,用的人也太少了~~

以後多推廣推廣單元素枚舉這種更好的單例實現方式。在項目中的代碼開始修改實施

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章