建議:用私有構造器或者列舉型別強化Singleton屬性。
Singleton指僅僅被例項化一次的類。
Singleton通常被用來代表那些本質上唯一的系統元件,比如視窗管理器或者檔案系統。使類成為Singleton會使它的客戶端測試變得十分困難,因為無法給Singleton替換模擬實現,除非它實現一個充當其型別的介面。
在Java1.5發行版本之前,實現Singleton有兩種方法。這兩種方法都要把構造器保持為私有的,並匯出共有的靜態成員,以便允許客戶端能夠訪問該類的唯一例項。
在第一種方法中,共有靜態成員是個final域:
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis(){...}
public void leaveTheBuilding(){...}
}
私有構造器僅被呼叫一次,用來例項化公有的靜態final域Elvis.INSTANCE。由於缺少公有的或者受保護構造器,所以保證了Elvis的全域性唯一性:一旦Elvis類被例項化,只會存在一個Elvis例項。但要提醒一點:享有特權的客戶端可以藉助AccessibleObject.setAccessible方法,通過反射機制呼叫私有構造器。如果需要抵禦這種攻擊,可以修改構造器,讓它在被要求建立第二個例項的時候丟擲異常。
在實現Singleton的第二種方法中,公有的成員是個靜態工廠方法:
// 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(){...}
}
公有域方法的主要好處在於,組成類的成員的宣告很清楚地表明瞭這個類是一個Singleton:公有的靜態域是final的,所以該域將總是包含相同的物件引用。公有域方法在效能上不再有任何優勢:現在的JVM實現幾乎都能夠將靜態工廠發的呼叫內聯化。
工廠方法的優勢之一在於,它提供了靈活性:在不改變其API的前提下,我們可以改變該類是否應該為Singleton的想法。工廠方法返回該類的唯一例項,但是,他可以很容易被修改,比如改成為每個呼叫該方法的執行緒返回一個唯一的例項。第二個優勢與泛型有關。
為了使利用這其中一種方法的Singleton類變成可序列化的(Serializable),僅僅在宣告彙總加上“implements Serializable”是不夠的。為了維護並保證Singleton,必須宣告所有的例項域都是瞬時(transient)的,並提供一個readResolve方法。否則每次反序列化一個序列化的例項時,都會建立一個新的例項。在我們的例子中,會導致“假冒的Elvis”。為了防止這種情況,要在Elvis勒種加入下面這個readResolve方法:
// readResolve method to preserve singleton property
private Object readResolve() {
// Return the one true Elvis and let the garbage collector
// take care of the Elvis impersonator.
return INSTANCE;
}
從Java1.5發行版本起,實現Singleton還有第三種方法。只需編寫一個包含單個元素的列舉型別:
// Enum singleton - the preferred approach
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {...}
}
這種方法在功能上與公有域相似,但是它更加簡潔,無償的提供了序列化機制,絕對防止多次例項化,即使是在面對複雜的序列化或者反射攻擊的時候。雖然這種方法還沒有廣泛採用,但是單元素的列舉型別已經成為實現Singleton的最佳方法。
相關文章
- 3、用私有構造器或者列舉型別強化SIngleton屬性型別
- Effective Java筆記第一章建立和銷燬物件第三節用私有構造器或者列舉型別強化Singleton屬性Java筆記物件型別
- Effective Java - 構造器私有、列舉和單例Java單例
- 協議中的私有屬性協議
- laravel 使用schema構建器,新增構建表的列型別Laravel型別
- 列舉型別型別
- ENUM列舉型別型別
- java列舉型別Java型別
- java 列舉型別Java型別
- odoo欄位屬性列舉Odoo
- Java雙屬性列舉使用Java
- 屬性列舉法(轉載)
- javascript基礎(物件,物件屬性,屬性基本和引用資料型別,字面量建立物件,垃圾回收,屬性的列舉)(十三)JavaScript物件資料型別
- C#例項構造器,型別構造器 -筆記型別筆記
- 私有屬性
- 【C++】構造資料型別--陣列C++資料型別陣列
- 深度分析 Java 的列舉型別:列舉的執行緒安全性及序列化問題Java型別執行緒
- Java - Enum 列舉型別Java型別
- 常用的瀏覽器私有屬性瀏覽器
- 構建一個 Javascript 多維陣列構造器JavaScript陣列
- javascript哪些屬性是可列舉的JavaScript
- js可列舉屬性的遍歷JS
- 【四】使用列舉和結構來建立值型別型別
- [C++]結構體、共同體和列舉型別C++結構體型別
- PLC結構化文字(ST)——變數型別和變數屬性變數型別
- java中的列舉型別Java型別
- JavaSE基礎:列舉型別Java型別
- mysql之ENUM列舉型別MySql型別
- Rust的列舉型別EnumRust型別
- 小議如果構造陣列陣列
- 【C++】資料型別-列舉型C++資料型別
- TypeScript 中列舉型別的理解?應用場景?TypeScript型別
- JavaScript私有屬性和靜態屬性JavaScript
- 結構體-簡單列舉型別——植物與顏色結構體型別
- 資料結構複習-01enum列舉型別資料結構型別
- C++ 列舉型別介紹C++型別
- 列舉型別分享 第五節型別
- java基礎(十一) 列舉型別Java型別