枚举单例避免线程不同步、反射攻击和序列化攻击的原理总结
引言
最近在学习单例模式的多种实现方法,单例的重点有4个:
1.是否懒加载
2.是否线程同步
3.反射攻击
4.序列化攻击
其中枚举单例除了不能懒加载,可以实现线程同步,防止反射攻击和序列化攻击。
大部分的博客,只是用代码论证了枚举单例确实避免了这些问题,却并未完整说出这3个问题是如何避免的,只说出了其中的一两个,所以我经过漫长的资料查找后,终于整理出了总结。
枚举单例写法
——《Effective Java》
enum SingletonDemo{
INSTANCE;
public void otherMethods(){
System.out.println("Something");
}
}
如何避免线程不同步?
枚举类编译后,所有的枚举变量都是static,通过类加载机制实现线程同步。
如何避免反射攻击?
反射无法通过newInstance调用构造器来创建新的枚举类实例。(详细请参考为什么要用枚举实现单例模式(避免反射、序列化问题))
如何避免序列化攻击?
Java规范字规定,每个枚举类型及其定义的枚举变量在JVM中都是唯一的,因此在枚举类型的序列化和反序列化上,Java做了特殊的规定。
在序列化的时候Java仅仅是将枚举对象的name属性输到结果中,反序列化的时候则是通过java.lang.Enum的valueOf()方法来根据名字查找枚举对象。
比如说,序列化的时候只将DATASOURCE
这个名称输出,反序列化的时候再通过这个名称,查找对应的枚举类型,因此反序列化后的实例也会和之前被序列化的对象实例相同。(详细请参考枚举单例 最安全的单例,可以有效防止反射,序列化)
public enum DataSourceEnum {
DATASOURCE;
}