本章的主題是創建和銷毀對象:何時以及如何創建對象,何時以及如何避免創建對象,如何確保它們能夠適時地銷毀,以及如何管理對象銷毀之前必須進行的各種清理動作。
[toc]
Singleton 指僅僅被實例化一次的類。
使類成為Singleton會使它的客戶端測試變得十分困難,因為無法給 Singleton 替換模擬實例,除非它實現一個充當其類型的接口。
在 Java 1.5 發行版之前,實現 Singleton 有兩種方法。
這兩種方法都要把構造器保持為 私有的 ,并導出 公有的 靜態成員,以便允許客戶端能夠訪問該類的唯一實例。
公有域(public-field)方法:公有靜態成員是個 final 域。
//Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis( );
private Elvis( ) { ... }
public void leaveTheBuilding( ) { ... }
}
私有構造器僅被調用一次,用來實例化 公有的靜態final域 Elvis.INSTANC 。由于缺少公有或者受保護的構造器,保證了全局唯一性,但是可以通過反射方法,調用私有構造器。如果要抵御反射攻擊,可以修改構造器,在創建第二個實例的時候拋出異常
優點:
- 組成類的成員的聲明很清楚的表明了這個類是一個 Singleton 【公有靜態域是 final 的】。
- 公有域方法在性能上不再有任何優勢【現代JVM實現幾乎都能夠將靜態工廠方法的調用內聯化】。
靜態工廠方法 :公有的成員是個靜態工廠方法。
// 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( ) { ... }
}
對于靜態方法 Elvis.getInstance 的所有調用,都會返回同一個對象引用,仍要注意反射攻擊
優勢:
- 提供了靈活性:再不改變 API 的前提下,可以改變該類是否應該為 Singleton 的想法。
【比如改成每個調用該方法的現成返回一個唯一實例】 - 泛型優勢:根據調用方法時的 形式類型參數,返回 形式類型參數 類型的實例,避免多次創建實例
【如果之前了解過泛型,可以看書 114頁下方~115頁的泛型單例工廠模式介紹,泛型的相關術語在書 102頁的表格中,本與本章節內容不符,不贅述】
為了使利用以上方法實現的Singleton類變成是可序列化的(Serializable)(見第 11 章),僅僅在聲明上加上“implements Serializable”是不夠的。必須聲明所有實例域都是瞬時(transient)的,并提供一個 readResolve 方法。這方面的資料有很多,篇幅原因,請自善用搜索,這里提供一個我當時查閱的:點我查閱
而從 Java 1.5 發行版起,實現 Singleton 還有第三種方法。
編寫一個包含單個元素的枚舉類型:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding( ) { ... }
}
這種方法在功能上與公有域方法相近,但是它更加簡潔,無償地提供了序列化機制,絕對防止多次實例化,即使是在面對復雜的序列化或者反射攻擊的時候。雖然這種方法還沒有廣泛采用,但是單元素的枚舉類型已經成為實現 Singleton 的最佳方法。