ArrayList中为什么用transient修饰数组?
ArrayList 中使用 transient 关键字修饰数组 elementData 是为了控制对象的序列化过程以提高性能和节省存储空间。
- 性能和效率:elementData 数组是 ArrayList 的内部数据结构,它可能会非常大,但实际上只有一部分用于存储元素。如果不使用 transient 修饰符,整个 elementData 数组将会被序列化,包括未使用的部分,降低了序列化效率。
- 节省内存: 序列化整个 elementData 数组可能会占用大量的内存空间,尤其是当 ArrayList 很大时。使用 transient 可以避免序列化未使用的数组部分,从而节省内存。
- 隐藏内部实现细节:ArrayList 的设计目标之一是封装其内部实现细节,以防止直接访问和依赖于内部数据结构。使用 transient 可以确保 elementData 不会被序列化,从而保护了 ArrayList 的封装性。
如何序列化 ArrayList?
ArrayList通过两个方法readObject、writeObject自定义序列化和反序列化策略,实际直接使用两个流ObjectOutputStream和ObjectInputStream来进行序列化和反序列化。
private void writeObject(ObjectOutputStream var1) throws IOException {
// 保存当前 modCount 的值,用于后续检查
int var2 = this.modCount;
// 执行默认对象序列化,保存除 elementData 数组以外的状态
var1.defaultWriteObject();
// 写入 ArrayList 的大小(元素数量)
var1.writeInt(this.size);
// 遍历 elementData 数组,并逐个写入元素
for(int var3 = 0; var3 < this.size; ++var3) {
var1.writeObject(this.elementData[var3]);
}
// 检查 modCount 是否发生了变化,如果变化了,抛出异常
if (this.modCount != var2) {
throw new ConcurrentModificationException();
}
}
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
// 将 elementData 初始化为空数组
this.elementData = EMPTY_ELEMENTDATA;
// 执行默认对象反序列化,还原除 elementData 数组以外的状态
var1.defaultReadObject();
// 读取 ArrayList 的大小(元素数量),但未使用此值
var1.readInt();
// 如果 ArrayList 的大小大于 0,执行以下操作
if (this.size > 0) {
// 确保 elementData 数组具有足够的容量来容纳元素
this.ensureCapacityInternal(this.size);
// 获取 elementData 数组的引用
Object[] var2 = this.elementData;
// 从输入流中逐个读取元素并存储到 elementData 数组中
for(int var3 = 0; var3 < this.size; ++var3) {
var2[var3] = var1.readObject();
}
}
}