設計模式|單例模式(3) Enum列舉單例
阿新 • • 發佈:2018-12-18
上篇文章已經討論了單例模式的安全問題。而列舉型別的單例模式是實現單例模式的最好的方法
參考:《Effective Java中文版》 p14-p15
只需編寫一個包含單個元素的列舉型別。
程式碼
列舉類
public enum EnumSingleton {
INSTANCE;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public static EnumSingleton getInstance(){
return INSTANCE;
}
}
測試類
/**
* 列舉型別的單例模式測試
*/
@org.junit.Test
public void test08(){
EnumSingleton enumSingleton = EnumSingleton.getInstance();
enumSingleton.setData("we");
System.out.println(enumSingleton); // INSTANCE
EnumSingleton enumSingleton1 = EnumSingleton.getInstance();
System.out.println(enumSingleton1); // INSTANCE
System.out.println(enumSingleton1.getData()); //we
System.out.println(enumSingleton == enumSingleton1); //true
}
反序列化攻擊測試
/**
* 列舉型別的單例模式測試:反序列化攻擊
*/
@org.junit.Test
public void test09() throws Exception{
/**
* 正常獲取例項
*/
EnumSingleton enumSingleton = EnumSingleton.getInstance();
enumSingleton.setData("we");
System.out.println(enumSingleton.getData()); // we
/**
* 反序列化獲取例項
*/
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("enumSingleten"));
oos.writeObject(enumSingleton);
File file = new File("enumSingleten");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
EnumSingleton newInstance = (EnumSingleton)ois.readObject();
System.out.println(newInstance.getData()); // we
/**
* 比較
*/
System.out.println(newInstance.getData() == enumSingleton.getData());//true
}
同一個例項
反射攻擊例項
/**
* 列舉型別的單例模式測試:反射攻擊
*/
@org.junit.Test
public void test10() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
/**
* 正常獲取例項
*/
EnumSingleton enumSingleton = EnumSingleton.getInstance();
enumSingleton.setData("we");
System.out.println(enumSingleton.getData());
/**
* 反射獲取例項
*/
Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
Constructor<EnumSingleton> constructor = enumSingletonClass.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
/**
* 如果newInstance()方法沒有引數java.lang.NoSuchMethodException
* 具體可看反射對列舉型別的處理方式
*/
/**
* 報錯:
* java.lang.IllegalArgumentException: Cannot reflectively create enum objects
* 不能反射enum型別
*/
EnumSingleton newInstance = constructor.newInstance("we",1);
System.out.println(newInstance.getData());
}
通過上面可以看到列舉型別可以防止反射攻擊。
擴充套件1:在列舉類中聲名方法
列舉類
public enum EnumSingleton{
INSTANCE{
protected void printTest(){
System.out.println("test");
}
};
protected abstract void printTest();
}
測試
/**
* 列舉類呼叫方法
*/
@org.junit.Test
public void test11(){
EnumSingleton enumSingleton = EnumSingleton.getInstance();
enumSingleton.printTest();
}