1. 程式人生 > >(3):用私有構造器或者列舉型別強化Singleton屬性

(3):用私有構造器或者列舉型別強化Singleton屬性

在java 1.5版本發行前實現單例一般有兩種方法

(1)靜態成員

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    public void leaveTheBuilding() {}

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}
私有構造器僅被呼叫一次,用來初始化公有的靜態final域Elvis.INSTANCE。沒有公有的或者受保護的構造器,確保了Elvis的全域性唯一性:一旦初始化了Elvis類,將只存在一個Elvis例項,不多也不少。客戶端的任何行為都不會改變這一點,但要提醒一點:享有特權的客戶端可以藉助於AccessibleObject.setAccessible方法,通過反射機制(見第53條)呼叫私有構造器。如果需要抵禦這種攻擊,可以修改構造器,讓它在被要求建立第二個例項的時候丟擲異常。
(2)靜態工廠方法
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();

    private Elvis() {
    }

    public static Elvis getInstance() {
        return INSTANCE;
    }

    public void leaveTheBuilding() {}
    
    public static void main(String[] args) {
        Elvis elvis = Elvis.getInstance();
        elvis.leaveTheBuilding();
    }
}
工廠方法的主要優勢在於,它提供了靈活性:在不改變其API的前提下,我們可以改變該類是否應該為Singleton的想法。工廠方法返回該類的唯一例項,但是,它可以很容易被修改,比如改成為每個呼叫該方法的執行緒返回一個唯一的例項。

在java 1.5後出現了一個新的方法

(3)單元素列舉型別

public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {}

    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}
這種方法在功能上與公有域方法相近,但是它更加簡潔,無償地提供了序列化機制,絕對防止多次例項化,即使是在面對複雜的序列化或者反射攻擊的時候。雖然這種方法還沒有被廣泛採用,但是單元素的列舉型別已經成為實現Singleton的最佳方法。