文章目录
- 1. 核心矛盾:空间浪费
- 2. 为什么加 `transient`?
- 3. 如何“理解”图片中的 `writeObject` 代码?
- 4. 这样做的好处(总结)
- 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循环):for(inti=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)给剔除掉。
这个“按需打包”的思路,你觉得在你自己写代码时,还有哪些场景能用上?