[Effective Java] (03)用私有構(gòu)造器或者枚舉類型強化Singleton

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的最佳方法。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容