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代而永遠得不到回收的缺點(大多數記憶體回收演算法是這樣)。
枚舉:很好,不僅能避免多線程同步問題,而且還能防止還原序列化重新建立新的對象。但是失去了類的一些特性,沒有消極式載入,用的人也太少了~~
以後多推廣推廣單元素枚舉這種更好的單例實現方式。在項目中的代碼開始修改實施