Effective java第三條:用私有構造器或者列舉型別強化singleon屬性
阿新 • • 發佈:2019-02-16
單例模式大家都不模式,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異常。
單元素列舉型別實現單例是最方便的。