writeReplace 是 Java 序列化中用于在序列化前动态替换对象的私有方法,签名必须为 private Object writeReplace() throws ObjectStreamException,返回可序列化对象以实现脱敏、协议降级等目的。

Java 中的 writeReplace 方法是序列化机制提供的一个钩子,允许你在对象被序列化前,用另一个对象**动态替换**它。这不用于“修改变量”,而是用一个**新对象替代原对象参与序列化**,从而间接实现传输中对数据形态的控制(比如脱敏、降级、适配旧协议等)。
writeReplace 的核心作用与触发时机
writeReplace 是一个私有、无参、返回任意 Object 的方法。当 JVM 序列化器(如 ObjectOutputStream)准备写入某个对象时,会**反射检查该对象所属类是否定义了该方法**;若存在且可访问,就调用它,并将返回值作为真正要序列化的对象 —— 原对象本身不会进入字节流。
注意:它不改变原对象在内存中的状态,也不影响反序列化逻辑(除非配合 readResolve)。
典型使用场景与实现方式
常见用途包括:敏感字段脱敏、兼容性版本转换、懒加载代理替换为真实数据、移除不可序列化成员等。
- 脱敏示例:用户对象含明文密码,序列化前替换成仅含用户名的轻量对象
-
协议降级:新版本类新增字段,但需向老服务发送旧格式,
writeReplace返回一个只含老字段的 DTO 实例 -
规避不可序列化依赖:对象持有一个
ThreadLocal或Socket,通过 replace 返回一个不含这些字段的副本
正确编写 writeReplace 的关键点
必须满足以下条件,否则会被忽略或抛异常:
- 方法签名严格为:
private Object writeReplace() throws ObjectStreamException - 返回对象自身也必须可序列化(即实现
Serializable),否则序列化失败 - 不要在方法内修改原对象状态(非必需,且易引发并发问题)
- 若替换后对象需控制反序列化行为(如单例、还原逻辑),应配套实现
readResolve
一个完整可运行的例子
假设有个 User 类,传输时需隐藏邮箱:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String email;
public User(String name, String email) {
this.name = name;
this.email = email;
}
private Object writeReplace() {
// 替换为仅含 name 的匿名序列化友好的对象
return new Serializable() {
private static final long serialVersionUID = 2L;
private final String name = User.this.name;
@Override
public String toString() {
return "User{name='" + name + "'}";
}
};
}
}
这样,无论原 User 多复杂,网络上传输的只是那个精简对象 —— 接收方拿到的就是没有 email 字段的实例。

















