枚举单例可天然防反射与反序列化攻击,因其初始化由jvm在类加载阶段完成且不可绕过;而静态块+私有构造器方案必须额外加入构造器内校验逻辑、final静态字段及readresolve方法才能实现同等防护。

静态初始化块配合私有构造器本身不能防反射,必须额外加入运行时校验逻辑——这是关键前提。单纯靠 private 构造和 static{} 块,反射仍可绕过访问控制创建新实例。
为什么默认组合不防反射
Java 反射可通过 setAccessible(true) 强行调用私有构造器,而静态块只在类加载时执行一次,对后续反射调用无约束力。若构造器内无状态检查,每次反射调用都会生成全新对象,彻底破坏单例语义。
在私有构造器中加入实例存在性校验
核心防御动作:首次构造成功后记录标志位(如静态 volatile 字段或 AtomicBoolean),后续任何构造尝试都抛异常:
- 声明一个 static volatile boolean initialized = false
- 在私有构造器开头判断 if (initialized) throw new RuntimeException("Singleton already initialized")
- 在构造逻辑末尾设 initialized = true
- 静态块中完成实例创建,并确保该块仅在类加载时执行(即只触发一次构造)
配合 final 静态字段 + 静态块实现基础单例骨架
确保实例引用不可变、初始化时机可控:
- 声明 private static final YourSingleton INSTANCE
- 在 static{} 块中执行 INSTANCE = new YourSingleton()
- 构造器内完成上述校验逻辑,防止反射二次构造
- 提供 public static YourSingleton getInstance() 返回 INSTANCE
补充反序列化防护(readResolve)
若类实现 Serializable,还需定义 readResolve 方法,否则反序列化会生成新对象:
- 添加 private Object readResolve() { return INSTANCE; }
- 该方法在反序列化完成后被 JVM 自动调用,强制返回已有实例
这样组合后,既利用静态块保证类加载期单次初始化,又靠构造器校验封堵反射入口,再借 readResolve 拦截反序列化路径——三者协同才构成较完整的防反射单例方案。










