单例模式的序列化安全需通过显式定义readResolve()方法实现,该方法在反序列化后返回静态实例,确保唯一性;枚举实现天然安全,禁用序列化或自定义writeReplace()可作补充方案。

单例模式的序列化安全,核心在于防止反序列化过程创建新实例,从而破坏“全局唯一”的契约。
为什么序列化会破坏单例?
Java 默认的反序列化机制会绕过构造方法,直接调用 ObjectInputStream 的底层逻辑重建对象。即使构造器私有、类中只有一份静态实例,反序列化仍可能生成一个全新的对象——它和原单例在内存中地址不同,完全独立。
这意味着:
- 两次调用 getInstance() 返回的是同一个对象;
- 但一次 getInstance() 和一次 反序列化读取 得到的却是两个不同对象;
- 单例性就此失效。
如何实现序列化安全?
最可靠且简洁的方式是:让单例类实现 Serializable 接口,并**显式定义 readResolve() 方法**。
该方法会在反序列化完成后被 JVM 自动调用,其返回值将替代刚刚创建的对象,成为最终反序列化的结果。只要让它返回静态单例实例,就能确保反序列化也指向唯一对象。
示例(以双重校验锁单例为例):
public class Singleton implements Serializable {private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
// 关键:保证反序列化不破坏单例
private Object readResolve() {
return getInstance();
}
}
其他可行但需注意的方案
- 使用枚举实现单例:JVM 保证枚举类型的序列化/反序列化天然安全,无需额外处理,且能天然防御反射攻击;
- 禁止序列化:在类中定义 writeObject() 和 readObject() 并抛出 NotSerializableException,适用于明确不需要持久化的场景;
- 自定义序列化逻辑:重写 writeReplace() 返回单例引用,配合 readResolve() 双保险,适合高安全性要求系统。
别忘了配套防护
仅解决序列化还不够。真实项目中常需组合防御:
- 私有构造器防止外部 new;
- volatile + DCL 或静态内部类保障线程安全;
- readResolve() 拦截反序列化;
- 必要时通过枚举或反射检查(如构造器内加 flag)防反射破解。










