本章的主題是創(chuàng)建和銷毀對象:何時以及如何創(chuàng)建對象拴泌,何時以及如何避免創(chuàng)建對象抛猖,如何確保它們能夠適時地銷毀压语,以及如何管理對象銷毀之前必須進行的各種清理動作吁朦。
[toc]
Singleton 指僅僅被實例化一次的類榄鉴。
使類成為Singleton會使它的客戶端測試變得十分困難履磨,因為無法給 Singleton 替換模擬實例,除非它實現(xiàn)一個充當其類型的接口庆尘。
在 Java 1.5 發(fā)行版之前剃诅,實現(xiàn) Singleton 有兩種方法。
這兩種方法都要把構造器保持為 私有的 驶忌,并導出 公有的 靜態(tài)成員矛辕,以便允許客戶端能夠訪問該類的唯一實例。
公有域(public-field)方法:公有靜態(tài)成員是個 final 域付魔。
//Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis( );
private Elvis( ) { ... }
public void leaveTheBuilding( ) { ... }
}
私有構造器僅被調用一次聊品,用來實例化 公有的靜態(tài)final域 Elvis.INSTANC 。由于缺少公有或者受保護的構造器几苍,保證了全局唯一性杨刨,但是可以通過反射方法,調用私有構造器擦剑。如果要抵御反射攻擊妖胀,可以修改構造器芥颈,在創(chuàng)建第二個實例的時候拋出異常
優(yōu)點:
- 組成類的成員的聲明很清楚的表明了這個類是一個 Singleton 【公有靜態(tài)域是 final 的】。
- 公有域方法在性能上不再有任何優(yōu)勢【現(xiàn)代JVM實現(xiàn)幾乎都能夠將靜態(tài)工廠方法的調用內聯(lián)化】赚抡。
靜態(tài)工廠方法 :公有的成員是個靜態(tài)工廠方法爬坑。
// Singleton with static factory
public class Elvis {
private static final Elvis INSTANCE = new Elvis( );
private Elvis( ) { ... }
public static Elvis getInstance( ) { return INSTANCE; }
public void leaveTheBuilding( ) { ... }
}
對于靜態(tài)方法 Elvis.getInstance 的所有調用,都會返回同一個對象引用涂臣,仍要注意反射攻擊
優(yōu)勢:
- 提供了靈活性:再不改變 API 的前提下盾计,可以改變該類是否應該為 Singleton 的想法。
【比如改成每個調用該方法的現(xiàn)成返回一個唯一實例】 - 泛型優(yōu)勢:根據(jù)調用方法時的 形式類型參數(shù)赁遗,返回 形式類型參數(shù) 類型的實例署辉,避免多次創(chuàng)建實例
【如果之前了解過泛型,可以看書 114頁下方~115頁的泛型單例工廠模式介紹岩四,泛型的相關術語在書 102頁的表格中哭尝,本與本章節(jié)內容不符,不贅述】
為了使利用以上方法實現(xiàn)的Singleton類變成是可序列化的(Serializable)(見第 11 章)剖煌,僅僅在聲明上加上“implements Serializable”是不夠的材鹦。必須聲明所有實例域都是瞬時(transient)的,并提供一個 readResolve 方法耕姊。這方面的資料有很多桶唐,篇幅原因,請自善用搜索茉兰,這里提供一個我當時查閱的:點我查閱
而從 Java 1.5 發(fā)行版起尤泽,實現(xiàn) Singleton 還有第三種方法。
編寫一個包含單個元素的枚舉類型:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding( ) { ... }
}
這種方法在功能上與公有域方法相近规脸,但是它更加簡潔坯约,無償?shù)靥峁┝诵蛄谢瘷C制,絕對防止多次實例化燃辖,即使是在面對復雜的序列化或者反射攻擊的時候鬼店。雖然這種方法還沒有廣泛采用网棍,但是單元素的枚舉類型已經成為實現(xiàn) Singleton 的最佳方法黔龟。