1. 程式人生 > >Effective java第三條:用私有構造器或者列舉型別強化singleon屬性

Effective java第三條:用私有構造器或者列舉型別強化singleon屬性

單例模式大家都不模式,java1.5發行版之前大家都用兩種方法實現singleton。

第一種:靜態成員

public class Singleton1 {
    public static final Singleton1 INSTANCE = new Singleton1();
    private Singleton1() {

    }
}

第二種:靜態工廠方法

public class Singleton2 {
    private static final Singleton2 INSTANCE = new Singleton2();
    private
Singleton2() { } public static Singleton2 getInstance() { return INSTANCE; } }

這兩種實現方式都有缺陷,享有特權的AccessibleObject.setAccessible()方法可以通過反射機制呼叫private成員變數,可以參考這篇文章。如果需要抵禦這種攻擊,可以在構造方法內判斷建立第二個例項的時候丟擲異常

如果上面兩種方法實現的Singleton是可以序列化的,加上 implements Serializable只保證它可以序列化,為了保證反序列化的時候,例項還是Singleton,必須宣告所有的例項域都是transient的,並且提供 readResolve方法,否則,每次反序列化都會生成新的例項。

public class Singleton1 implements Serializable {
    private static final Singleton1 INSTANCE = new Singleton1();
    private Singleton1() {}
    public void say() {
        System.out.println("HAHAHAHHAH");
    }
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
    public
static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { Singleton1 singleton1 = Singleton1.getInstance(); singleton1.say(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton.txt")); oos.writeObject(singleton1); oos.close(); // 反序列化的時候就會生成新的例項 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Singleton.txt")); Singleton1 singleton2 = (Singleton1)ois.readObject(); ois.close(); System.out.println(singleton1 == singleton2); } } 輸出結果: HAHAHAHHAH false

加入readResolve方法:

public class Singleton1 implements Serializable {
    private static final Singleton1 INSTANCE = new Singleton1();
    private Singleton1() {}
    public void say() {
        System.out.println("HAHAHAHHAH");
    }
    public static Singleton1 getInstance() {
        return INSTANCE;
    }
    private Object readResolve() {
        return INSTANCE;
    }
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        Singleton1 singleton1 = Singleton1.getInstance();
        singleton1.say();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Singleton.txt"));
        oos.writeObject(singleton1);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Singleton.txt"));
        Singleton1 singleton2 = (Singleton1)ois.readObject();
        ois.close();
        System.out.println(singleton1 == singleton2);
    }
}
輸出結果:
HAHAHAHHAH
true

第三種:單元素列舉型別

public enum Singleton2 {
     INSTANCE;

    public void say() {
        System.out.println("HAHAHAHHAH");
    }

    public static void main(String[] args) {
        Singleton2 singleton2 = Singleton2.INSTANCE;
        singleton2.say();
    }
}

通過列舉實現Singleton更加簡潔,同時列舉型別無償地提供了序列化機制,可以防止反序列化的時候多次例項化一個物件。列舉型別也可以防止反射攻擊,當你試圖通過反射去例項化一個列舉型別的時候會丟擲IllegalArgumentException異常。
單元素列舉型別實現單例是最方便的。