跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何实现Java的自定义序列化逻辑_writeObject与readObject用法

为什么
writeObject
和
readObject
必须是 private 且带 ObjectInputStream/ObjectOutputStream 参数 因为 JVM 在反序列化时会通过反射强制调用这两个方法,但只认特定签名:必须是
private void writeObject(ObjectOutputStream)
和
private void readObject(ObjectInputStream)
。哪怕加个
static
、改个参数名、或者换成
throws IOException
(实际已声明),JVM 就直接跳过,退回到默认序列化逻辑。 常见错误现象:
NotSerializableException
突然出现,或字段值在反序列化后变成
null
/0,其实是因为自定义方法没被识别,而你又把
serialVersionUID
写死了,导致看似“成功”实则漏数据。 不要加
public
或
protected
—— 反射调用要求严格 private 不要试图重载:比如再写一个
writeObject(OutputStream)
,JVM 不认 如果类实现了
Externalizable
,这两个方法会被完全忽略——二者互斥 怎么在
writeObject
里跳过敏感字段又不破坏兼容性 典型场景:类里有临时缓存、数据库连接、或密码字段,不能序列化;但又不能删掉字段(否则老数据反序列化失败),也不能全靠
transient
(因为有些字段需要部分序列化,比如只存哈希值而非原始字符串)。 做法是在
writeObject
里手动控制输出顺序和内容,先调用
defaultWriteObject()
处理非 transient 字段,再额外写入自定义数据;
readObject
里按相同顺序读回,并跳过或重建敏感字段。 立即学习 “ Java免费学习笔记(深入) ”; Eclipse导入Android或其他的JAVA项目的正确方法 WORD版 本文档主要讲述的是Eclipse导入Android或其他的JAVA项目的正确方法;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看 下载
defaultWriteObject()
必须在自定义写逻辑前调用,否则非 transient 字段不会被保存 如果字段是
transient
,
defaultWriteObject()
自动跳过,你要手动
out.writeUTF(...)
补上 反序列化时,
defaultReadObject()
必须放在最前,否则字段初始化可能出错(尤其是 final 字段)
private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // 先写默认字段 out.writeLong(System.currentTimeMillis()); // 补时间戳 } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); // 必须先调 long timestamp = in.readLong(); // 再读自定义部分 }
readObject
中重建对象状态时最容易漏的三件事 反序列化不是构造函数调用,字段直写内存,所以所有依赖构造逻辑的状态(如集合初始化、监听器注册、懒加载缓存)都不会自动触发。如果忘了补,对象看起来“能用”,但一调方法就
NullPointerException
。 未初始化的集合字段(如
private List items;
)仍是
null
,得手动
items = new ArrayList<>();
final 字段如果没在构造器里赋值,反序列化后无法修改——只能靠
Unsafe
(不推荐)或改用非 final +
readObject
里赋值(JVM 允许) 如果有父类也参与序列化,且父类没实现自定义
readObject
,子类的
defaultReadObject()
不会自动调父类逻辑,得自己处理(通常靠父类提供受保护的初始化方法) 用
writeObject
/
readObject
后性能下降明显?别急着换方案 纯二进制序列化本身很快,慢通常是因为你在里面干了不该干的事:比如调远程接口、查数据库、解析大 JSON 字符串。这些操作在反序列化线程里同步执行,会卡住整个链路。 另一个隐形坑是日志:有人习惯在
readObject
里打全量字段日志,结果生产环境一跑批量反序列化,磁盘 I/O 直接拉满。 避免在
writeObject
/
readObject
中做任何阻塞或耗时操作 如果必须异步加载数据,请改为“懒加载 + 标记位”,首次访问时才触发,而不是在反序列化时硬等 Android 上注意:
ObjectInputStream
默认启用安全检查,大量反射会拖慢速度;可考虑用
sun.misc.Unsafe
替代(需权衡兼容性) 真正难的不是写对这两个方法,而是想清楚:这个类到底该不该支持 Java 原生序列化。很多问题,删掉
implements Serializable
就解决了。

相关文章