Singleton指僅僅被實例化一次的類,常用來代表那些本質(zhì)上唯一的系統(tǒng)組件(窗口管理器或者文件系統(tǒng));
- 使類成為Singleton會使它的客戶端測試變得十分困難,因為無法給Singleton替換模擬實現(xiàn),除非它實現(xiàn)一個充當其類型的接口。
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的全局唯一性,其可以通過反射機制,設置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支持序列化是不夠的,應為每次放序列化一個序列化的實例時,都會創(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();
}
}
該方法在功能上與公有域方法相近,但更加簡潔,無償提供序列化機制,在面對復雜的序列化或者反射攻擊的時候也能防止多次實例化;
- (Enum防止通過反射實例化原因未完待續(xù)··· |)
- (Enum反序列化機制未完待續(xù)···)
單個元素枚舉類型已經(jīng)成為實現(xiàn)Singleton的最佳方法。