java中保持单例序列化唯一性的关键是防止反序列化新建对象,核心手段是用readresolve()方法在反序列化末尾替换为原始实例,或直接采用jvm硬保障的枚举单例(public enum singleton { instance; })。

用 readResolve() 拦截并替换反序列化结果
这是对普通类(非枚举)最常用、最直接的防护方式。
- 在单例类中声明一个 private、无参数、返回类型为当前类的 readResolve 方法
- JVM 在反序列化完成、对象字段已填充后,会自动调用该方法,并用其返回值替代刚构建的对象
- 方法体只需简单返回唯一的静态实例(如
return INSTANCE;) - 必须实现
Serializable接口,否则该方法不生效;若实现Externalizable,则需改用readExternal
直接用枚举实现单例
这是 JVM 层面硬保障的终极方案,无需额外代码防护。
- Java 枚举的反序列化由 JVM 特殊处理:不走常规构造流程,而是直接从类加载时注册的枚举常量表中查找并返回已有引用
- 即使字节流被篡改、或手动定义了
readResolve,JVM 也会忽略,强制返回原实例 - 天然防反射、防序列化、防多类加载器场景下的重复初始化
- 写法极简:
public enum Singleton { INSTANCE; },调用直接用Singleton.INSTANCE
注意配套防御点
单靠序列化防护还不够,完整单例安全需组合考虑:
- 私有构造器里加运行时校验(如检测
INSTANCE != null),可防反射攻击 - 饿汉式或静态内部类方式保证类加载阶段的线程安全,但它们本身不解决序列化问题
- 如果使用 Spring 等容器管理单例 Bean,需确认其作用域配置为
singleton,且避免原型(prototype)误配
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











