Java枚举单例通过JVM硬性保障防止反序列化漏洞:序列化仅存名称,反序列化直接查Enum.valueOf返回唯一实例,不调用构造器、readResolve或初始化块,且ObjectInputStream的readEnum方法强制返回已有对象。

Java 枚举类在单例模式中防止反序列化漏洞,靠的是 JVM 层面的硬性保障,不是靠代码逻辑补漏。它不依赖 readResolve(),也不走常规反序列化流程,从机制上就杜绝了新实例的产生。
枚举反序列化不调用构造器
普通类反序列化时,JVM 会尝试还原对象状态,可能调用私有构造器——这就给了攻击者绕过单例的机会。而枚举类型被 JVM 特殊处理:
- 序列化时只保存枚举常量的名称(如
"INSTANCE")和类信息,不保存字段值或执行任何方法 - 反序列化时,JVM 直接调用
Enum.valueOf(类.class, 名称)查找已加载的静态常量 - 这个过程完全跳过构造器、初始化块、
readObject或readResolve,连入口都不开放
JVM 强制返回已有实例
ObjectInputStream 对枚举有专用方法 readEnum(),它的行为是硬编码的:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 不会新建对象,也不会触发类初始化
- 始终从 JVM 内部维护的枚举注册表中返回那个唯一实例
- 即使字节流被篡改(比如伪造一个不存在的 name),
Enum.valueOf()也会抛出IllegalArgumentException,绝不会返回非法对象
无需手动防御,写了也无效
你不需要、也不应该为枚举单例实现 readResolve():
立即学习“Java免费学习笔记(深入)”;
- JVM 明确忽略枚举类中的
readResolve方法,无论是否存在 - 枚举天然实现
Serializable,但它的序列化语义由平台定义,不受用户控制 - 试图用反射或第三方序列化框架(如 JSON)破坏它,本质是另一套机制——那不属于 Java 原生序列化的防护范围
验证方式很简单
写个测试就能确认是否真的安全:
- 序列化
Singleton.INSTANCE后再反序列化 - 用
==比较两个引用,结果一定是true - 对比普通单例(没写
readResolve),这里不会出现false

















