文章目录
-
-
- [1. 核心矛盾:空间浪费](#1. 核心矛盾:空间浪费)
- [2. 为什么加 `transient`?](#2. 为什么加
transient?) - [3. 如何"理解"图片中的 `writeObject` 代码?](#3. 如何“理解”图片中的
writeObject代码?) - [4. 这样做的好处(总结)](#4. 这样做的好处(总结))
- [5. 额外的一点:那行 `modCount != expectedModCount`](#5. 额外的一点:那行
modCount != expectedModCount)
-


这是一个关于 "性能优化" 和 "精准控制" 的非常经典的设计案例。
简单来说,ArrayList 里的 elementData 数组就像是一个有 100 个座位的电影院 ,但此时可能只有 10 个人在看电影。
1. 核心矛盾:空间浪费
- 默认序列化: 如果不加
transient,Java 会把整个elementData数组(100 个位置)全部序列化。这意味着它会花费空间和时间去记录后面那 90 个null。 - ArrayList 的扩容机制: 为了保证性能,
ArrayList往往会预留空间(比如你只存了 10 个元素,但它可能已经申请了 16 或 24 个位置)。
2. 为什么加 transient?
为了**"拒绝默认,手动接管"**。
标记为 transient 后,Java 的默认序列化机制就会无视这个数组。然后,ArrayList 开发者通过重写 writeObject 方法,实现了一套"按需打包"的方案。
3. 如何"理解"图片中的 writeObject 代码?
你可以把这段代码看作是一个精明的搬家公司经理:
-
s.defaultWriteObject(): 先把那些不是transient的普通属性(比如size变量)按规矩打包好。 -
s.writeInt(elementData.length): 记录一下当前的"总容量",这样回来复原时知道要开多大的房间。 -
核心逻辑(那个
for循环):javafor (int i=0; i<size; i++) s.writeObject(elementData[i]);- 重点: 它只循环到
size,而不是elementData.length。 - 效果: 电影院有 100 个座,经理只登记了坐了人的那 10 个位置。后面 90 个空位直接无视。
- 重点: 它只循环到
4. 这样做的好处(总结)
| 维度 | 默认序列化(不加 transient) | ArrayList 的做法(加 transient) |
|---|---|---|
| 文件大小 | 很大(包含大量 null) |
很小(只存有效数据) |
| 传输速度 | 慢(废话多) | 快(全是干货) |
| 恢复效率 | 略低 | 高 |
5. 额外的一点:那行 modCount != expectedModCount
图片代码里还有一行检查。这是为了防止在你"打包"的过程中,另一个线程突然改了 List 的内容。如果改了,它会直接抛出 ConcurrentModificationException 报错。这就是我们之前讨论的,它在努力保证操作过程的安全性。
一句话总结:
加 transient 不是为了不存数据,而是为了只存有效数据,把那些占地方的空位(null)给剔除掉。
这个"按需打包"的思路,你觉得在你自己写代码时,还有哪些场景能用上?