Java中自定义writeObject和readObject可绕过默认序列化机制,需实现Serializable接口并声明private方法;典型用途包括加密敏感字段、跳过非transient属性、兼容版本变更及反序列化后状态修复。

Java 中通过 writeObject 和 readObject 方法可以自定义对象的序列化与反序列化逻辑,绕过默认的字段逐个写入/读取机制。这在需要加密敏感字段、跳过 transient 字段以外的某些属性、兼容旧版本数据结构或执行初始化后处理时特别有用。
必须满足的基本前提
要让 JVM 调用你自定义的 writeObject 和 readObject,类必须:
- 实现
java.io.Serializable接口(哪怕只是标记接口) - 将两个方法声明为
private void writeObject(ObjectOutputStream out) throws IOException和private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException - 确保方法签名完全匹配(包括
private修饰符和异常声明),否则 JVM 会忽略它们,走默认逻辑
典型定制场景与写法
常见需求包括:隐藏敏感字段、动态计算字段不存、修复反序列化后的对象状态、兼容不同版本字段变化。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
跳过某个非 transient 字段的序列化:在
writeObject中只手动写出想保留的字段,不调用defaultWriteObject();对应地,在readObject中也不调用defaultReadObject(),而是按顺序读取并赋值 -
加密/解密字段:比如对密码字段加密后再写入,在
readObject中解密并还原 -
反序列化后重建资源或校验状态:例如恢复
transient的缓存、重新打开连接、检查字段合法性等,这些逻辑放在readObject末尾最安全
必须配合 defaultWriteObject / defaultReadObject 使用吗?
不一定。是否调用取决于你想保留多少默认行为:
立即学习“Java免费学习笔记(深入)”;
- 如果只想定制部分字段,推荐先调用
defaultWriteObject()写出所有非 transient 字段,再额外写入自定义内容(如加密值、时间戳);反序列化时先调defaultReadObject(),再读取额外数据 - 如果想完全接管(比如字段名变更、格式升级),可不调用默认方法,自己控制全部字段的写入顺序和读取逻辑——但要注意:
ObjectInputStream读取顺序必须严格对应ObjectOutputStream的写入顺序
容易踩的坑
这些细节不注意会导致反序列化失败或数据错乱:
-
writeObject和readObject必须是private,不能是public或protected,否则无效 - 不要在
readObject中直接给 final 字段赋值(JDK 9+ 会抛java.io.InvalidClassException),final 字段只能靠构造器或反序列化前的初始化保证 - 若类有父类且父类也实现了自定义
readObject,子类中需显式调用super.readObject(in)(前提是父类方法可访问);否则父类字段可能未被正确恢复 - 自定义方法里抛出的异常会被包装成
IOException或ClassNotFoundException向上传递,调试时注意看原始 cause

















