ArrayList序列化只保存有效元素,因其将elementData声明为transient跳过默认序列化,并通过自定义writeObject和readObject方法,仅序列化size个实际元素,避免冗余空间浪费。

Java 中 ArrayList 的序列化机制之所以能只序列化有效元素,核心在于它没有依赖 JVM 默认的序列化逻辑,而是通过自定义序列化方法 + transient 关键字配合实现的。
elementData 被声明为 transient
ArrayList 的底层存储数组 elementData 被显式标记为 transient:
- 这会跳过 JVM 默认的字段自动序列化流程,避免把整个数组(包括大量未使用的空位)一并写入字节流;
- 因为数组容量通常大于实际元素个数(
size),比如size = 5但elementData.length = 16,直接序列化整个数组会造成空间和时间浪费; -
transient并不表示“不序列化”,而是“不走默认序列化”,为手动控制留出空间。
自定义 writeObject 方法只写 size 个元素
ArrayList 在源码中实现了私有的 writeObject 方法,该方法被 JVM 序列化机制自动调用:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 先调用
defaultWriteObject()序列化非transient字段(如size、modCount等); - 再遍历
elementData[0]到elementData[size - 1],逐个调用out.writeObject(elementData[i]); - 这样最终写入流的,只有真正存在的
size个对象,与数组容量无关。
反序列化时重建 elementData 并还原有效数据
对应地,readObject 方法负责安全还原:
立即学习“Java免费学习笔记(深入)”;
- 先调用
defaultReadObject()恢复size等字段; - 根据
size创建新数组(或复用默认空数组),再逐个读取size个对象填入; - 确保反序列化后得到的 ArrayList 与原对象逻辑一致:元素顺序、数量、内容完全相同,且内部数组容量按需分配(不会保留原数组的冗余长度)。
为什么不用默认序列化?
如果去掉 transient 并依赖默认行为:
- 整个
elementData数组会被完整序列化,含大量null占位项; - 可能暴露内部实现细节(如扩容策略导致的稀疏数组);
- 增大序列化体积,降低网络传输或磁盘 IO 效率;
- 反序列化后可能产生不符合预期的数组长度,影响后续扩容判断逻辑。

















