Java中可通过重写private void writeObject/readObject方法自定义序列化,先调用defaultWriteObject/defaultReadObject处理默认字段,再手动加密、兼容版本变更或补充逻辑,需声明抛出IOException和ClassNotFoundException。

Java 中可以通过重写 writeObject 和 readObject 方法来自定义序列化与反序列化行为,而不是依赖默认机制。这种方式让你完全控制哪些字段被保存、如何加密敏感数据、如何兼容版本变更,甚至可以在序列化前后执行初始化或清理逻辑。
为什么需要自定义?
默认序列化会递归保存所有非 transient、非 static 字段,但实际开发中常遇到这些情况:
- 某些字段不应被序列化(比如临时缓存、数据库连接),仅用
transient不够灵活; - 需要对字段做预处理(如脱敏密码、压缩大字符串);
- 父类未实现
Serializable,但你想保留其部分状态; - 类结构发生变更(如字段重命名、类型调整),需兼容旧数据;
- 想在反序列化后自动触发校验或重建内部引用关系。
怎么写 writeObject 和 readObject?
这两个方法必须是 private void 签名,且必须定义在可序列化类内部(不能是继承来的)。JVM 在序列化/反序列化时会通过反射自动调用它们,无需手动注册。
-
writeObject(ObjectOutputStream out):先调用out.defaultWriteObject()保存默认字段,再手动写入自定义内容(如加密值、计算字段); -
readObject(ObjectInputStream in):先调用in.defaultReadObject()恢复默认字段,再手动读取并还原自定义逻辑(如解密、重建对象图)。
注意:两个方法都必须声明抛出 IOException 和 ClassNotFoundException,即使你没显式 throw。
立即学习“Java免费学习笔记(深入)”;
一个实用例子:保护敏感字段并兼容字段变更
假设有个用户类,旧版本只有 userName,新版本加了 fullName,且希望密码永远不进字节流:
private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject(); // 保存 userName、age 等常规字段
out.writeUTF(encrypt(password)); // 手动加密写入
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject(); // 恢复 userName、age
if (in.available() > 0) { // 兼容旧数据:可能没有 password 字段
password = decrypt(in.readUTF());
} else {
password = "default"; // 旧数据无此字段,设默认值
}
// 自动补全 fullName(兼容性逻辑)
if (fullName == null && userName != null) {
fullName = userName;
}
}
其他可选的自定义方式
除了上述最常用的方式,还有两种补充手段:
-
writeReplace()和readResolve():用于替换整个序列化对象(比如返回单例实例、包装代理对象),常用于单例模式防护或对象规范化; - 实现
Externalizable接口:比Serializable更彻底——你完全负责所有字段的读写,连类元信息都不自动写入,适合高性能或强控制场景,但开发成本更高。
不复杂但容易忽略


















