arraylist能直接序列化而linkedlist可能出问题,因其字段均实现serializable且无transient节点;linkedlist的node类包私有、未序列化且被标记transient,导致反序列化后元素丢失。

为什么 ArrayList 能直接序列化,而 LinkedList 却可能出问题?
因为 ArrayList 的字段全是可序列化的(Object[]、size),JVM 默认序列化机制能完整还原;LinkedList 内部用双向链表节点 Node,而 JDK 8+ 中这个类是包私有的、未实现 Serializable,且被标记为 transient —— 所以默认序列化后反序列化会丢失全部元素。
- 实操上,别依赖默认行为去序列化
LinkedList或自定义集合,尤其在 RPC、缓存、日志落盘等场景 - 如果必须用,优先替换成
ArrayList或ArrayDeque,它们序列化稳定、性能更好 - 真要保留
LinkedList,得重写writeObject和readObject,手动遍历链表写入/重建节点
自定义集合类加 implements Serializable 就够了吗?
不够。加接口只是“声明支持”,真正起作用的是字段是否可序列化、是否有自定义序列化逻辑、以及是否规避了不可序列化引用。
- 所有非
transient非static字段类型都必须可序列化,否则反序列化抛java.io.NotSerializableException - 含
Comparator、Supplier等函数式接口字段时,记得用transient+ 自定义readObject重建,否则 lambda 表达式可能因捕获上下文导致序列化失败 - 建议显式声明
private static final long serialVersionUID = 1L;,避免因类结构微调(比如加个字段)导致InvalidClassException
writeObject 和 readObject 怎么写才不踩坑?
这两个私有方法是 JVM 序列化协议的钩子,不是普通重载 —— 方法签名错一个字符(比如参数不是 ObjectOutputStream)就完全不会被调用,变成走默认逻辑。
- 必须严格匹配签名:
private void writeObject(ObjectOutputStream out) throws IOException - 开头一定要调
out.defaultWriteObject()(除非你彻底放弃父类/字段的默认序列化) - 反序列化时,
readObject里先调in.defaultReadObject(),再按顺序读你手动写的字段,顺序错会导致数据错位 - 别在
writeObject里调out.writeObject(this),会无限递归栈溢出
用 Externalizable 替代 Serializable 值不值得?
只在极少数场景划算:集合结构极其固定、字段极多、且对序列化体积/速度有硬性要求(比如高频网络传输的中间件)。日常开发基本不用。
立即学习“Java免费学习笔记(深入)”;
-
Externalizable要求你实现writeExternal/readExternal,且必须手动处理所有字段,包括父类的 —— 没有defaultWriteObject这种兜底 - 类升级时,新增字段不会自动跳过,旧版本反序列化新数据会直接抛
EOFException - 它绕过
serialVersionUID校验,但代价是完全失去向后兼容弹性
Serializable 的业务对象,或者用了某个内部类作为泛型实参。上线前务必做一次真实反序列化测试,别只看 ObjectOutputStream 能写出去。










