Singleton指僅僅被實例化一次的類流礁,常用來代表那些本質(zhì)上唯一的系統(tǒng)組件(窗口管理器或者文件系統(tǒng));
- 使類成為Singleton會使它的客戶端測試變得十分困難荞膘,因為無法給Singleton替換模擬實現(xiàn)崩泡,除非它實現(xiàn)一個充當(dāng)其類型的接口窟坐。
1. 實現(xiàn)Singleton常用方法
- 靜態(tài)成員(公有域方法)
public class Singleton {
public static final Singleton instance = new Singleton();
private Singleton() { }
}
- 靜態(tài)工廠方法
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() { }
public static Singleton getInstance() {
return instance;
}
}
注:該兩種方法并不能保證Singleton的全局唯一性弄息,其可以通過反射機制痊班,設(shè)置AccessibleObject.setAccessible(true)
,改變構(gòu)造器的訪問屬性,調(diào)用構(gòu)造器生成新的實例摹量;如:
Constructor<?> constructor = Singleton.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Singleton instance = (Singleton)constructor.newInstance();
為了抵御這種攻擊涤伐,可以修改構(gòu)造器,讓其在被要求創(chuàng)建實例時拋出異常缨称,如下代碼所示:
public class Singleton {
private static int count = 0;
private static final Singleton instance = new Singleton();
private Singleton() {
if(count > 0) {
throw new IllegalArgumentException("Cannot create Singleton twice!");
}
count++;
}
public static Singleton getInstance() {
return instance;
}
}
注:靜態(tài)變量count一定要在Singleton對象instance前面凝果,否則可以多調(diào)用一次構(gòu)造函數(shù)。
公有域方法與靜態(tài)工廠方法對比如下:
- 公有域方法主要好處在:這種方法很清晰的表明了這個類是Singleton具钥,公有的靜態(tài)域是final的豆村,所有該域?qū)⒖偘嗤膶ο笠茫?strong>不過公有域在性能上不再有任何優(yōu)勢:現(xiàn)代JVM實現(xiàn)幾乎將靜態(tài)工廠方法的調(diào)用內(nèi)聯(lián)化;
- 工廠方法的好處:提供了靈活性:可以在不改變其API的前提下骂删,改變該類是否為Singleton的想法。
2. 支持反序列化
僅僅通過implements Serializable
支持序列化是不夠的四啰,應(yīng)為每次放序列化一個序列化的實例時宁玫,都會創(chuàng)建一個新的實例,為防止這種情況發(fā)生柑晒,需添加readResolve
方法欧瘪,如:
為了維護Singleton,必須聲明所有的實例域都是瞬時(transient)的匙赞,并提供readResolve
方法佛掖。
public class Singleton implements Serializable{
private static int count = 0;
private static final Singleton instance = new Singleton();
private Singleton() {
if(count > 0) {
throw new IllegalArgumentException("Cannot create Singleton twice!");
}
count++;
System.out.println(count);
}
public static Singleton getInstance() {
return instance;
}
private Object readResolve() {
return instance;
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Singleton singleton = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton.QyQ"));
oos.writeObject(singleton);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Singleton.QyQ"));
Singleton o = (Singleton) ois.readObject();
ois.close();
System.out.println(singleton == o);
}
}
3. 單個元素枚舉類型實現(xiàn)Singleton
public enum Singleton {
instance;
public void leaveTheBuilding() {
System.out.println("Come on baby, QyQiaoo");
}
public static void main(String[] args) {
Singleton singleton = Singleton.instance;
singleton.leaveTheBuilding();
}
}
該方法在功能上與公有域方法相近,但更加簡潔涌庭,無償提供序列化機制芥被,在面對復(fù)雜的序列化或者反射攻擊的時候也能防止多次實例化;
- (Enum防止通過反射實例化原因未完待續(xù)··· |)
- (Enum反序列化機制未完待續(xù)···)
單個元素枚舉類型已經(jīng)成為實現(xiàn)Singleton的最佳方法坐榆。