ArrayList 在序列化时的重写重写了 writeObject()特殊处理的原因
在
ArrayList
中,elementData
是用来存储元素的内部数组,但它往往比实际元素多。
为了避免序列化多余的null
空位,JDK 把它标记为transient
,并通过重写writeObject()
/readObject()
方法,只序列化实际存在的元素,从而节省空间、提高效率。
一、什么是序列化(Serialization)
序列化是 Java 把对象转换成字节流,以便:
写入磁盘(持久化存储);
或通过网络传输。
反序列化(Deserialization)则是把字节流重新还原为对象。
要支持序列化,一个类必须实现 java.io.Serializable
接口。
例如:
class Person implements Serializable {String name;int age;
}
二、ArrayList
实现了 Serializable
接口
ArrayList
是可序列化的:
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable
但它在序列化时没有直接序列化整个内部数组,而是只序列化其中实际存放的元素部分。
这就是“自定义序列化逻辑”的由来。
三、为什么需要自定义序列化逻辑
ArrayList
底层是一个可变长数组:
transient Object[] elementData; // 存放元素的数组
private int size; // 实际元素个数
这里有两个关键点:
elementData
是一个 比实际元素数量更大的数组(为了预留扩容空间)。它被声明为
transient
,即:transient Object[] elementData;
transient的意思是:在默认序列化时不会被写入字节流中。
四、为什么 elementData
要用 transient
如果不加 transient
,Java 默认会序列化整个数组,包括:
有效元素;
多余的
null
空位。
举个例子:
ArrayList<String> list = new ArrayList<>(10);
list.add("A");
list.add("B");
此时:
size = 2
elementData.length = 10
若直接序列化整个 elementData
,就会把 8 个无用的 null
一起写进文件,不仅浪费空间,还没意义。所以,JDK 设计者用了 transient
,并重写了序列化方法,只序列化有效数据。
五、ArrayList
的自定义序列化方法
ArrayList
重写了 writeObject()
和 readObject()
方法,用来自定义序列化逻辑。
源码(简化版)如下:
private void writeObject(java.io.ObjectOutputStream s)throws java.io.IOException {int expectedModCount = modCount;// 执行默认序列化(不包含 transient 字段)s.defaultWriteObject();// 写入实际元素个数s.writeInt(size);// 写入有效元素for (int i = 0; i < size; i++) {s.writeObject(elementData[i]);}if (modCount != expectedModCount) {throw new ConcurrentModificationException();}
}