上篇文章已经讨论了单例模式的安全问题。而枚举类型的单例模式是实现单例模式的最好的方法
参考:《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();
}