需要自定义writeObject以避免序列化冗余或敏感的运行时状态(如modCount、threshold等),确保反序列化后状态一致、兼容性强、体积小且安全;典型做法是先defaultWriteObject再手动写出有效元素。

Java 中 Collection 接口的实现类(如 ArrayList、LinkedList、HashSet、HashMap 等)默认已实现 Serializable,但它们内部的 writeObject 方法通常被私有化并做了定制化序列化处理——不是简单地序列化所有字段,而是只序列化实际存储的数据和关键元信息,跳过 transient 字段或冗余状态,以保证兼容性、安全性和空间效率。
为什么需要自定义 writeObject?
Collection 实现类往往包含一些非数据性的运行时状态(如 modCount、threshold、loadFactor、空数组引用等),这些字段不应被序列化。否则会导致:反序列化后状态不一致、版本升级后兼容失败、序列化体积膨胀、甚至安全风险(如暴露内部结构)。因此 JDK 源码中普遍采用 private void writeObject(ObjectOutputStream s) throws IOException 手动控制序列化过程。
典型实现模式(以 ArrayList 为例)
ArrayList 的 writeObject 不序列化 elementData 数组本身(它可能包含 null 占位或容量冗余),而是只写入实际元素个数(size)和每个有效元素:
- 先调用
s.defaultWriteObject()序列化非 transient 的可序列化字段(如 size) - 再遍历
elementData[0] 到 elementData[size-1],逐个s.writeObject(e) - 跳过未使用的数组位置,避免写入 null 或脏数据
自定义集合类中如何正确实现 writeObject
如果你继承或封装 Collection 并希望支持序列化,需注意以下要点:
立即学习“Java免费学习笔记(深入)”;
- 声明
private static final long serialVersionUID = ...;显式固定版本号 - 将内部集合字段标记为
transient(如private transient List<t> data;</t>) - 提供
private void writeObject(ObjectOutputStream s) throws IOException方法,手动写出业务相关数据(如 size + 元素列表) - 配套实现
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException,按相同顺序恢复对象状态 - 避免在
writeObject中调用 public 方法(可能被子类重写,引发不可预知行为)
常见陷阱与建议
直接让自定义集合类实现 Serializable 但不重写 writeObject,很可能导致 transient 字段丢失、modCount 错乱、或反序列化后 size() != 0 但遍历时无元素等问题。
若集合封装了不可序列化对象,请确保其类型也实现了 Serializable,或提前转换/过滤;必要时可在 writeObject 中做预处理(如 deep copy、脱敏、懒加载置空)。
不复杂但容易忽略。


















