Skip to content

面试速答(先看这里)

**一句话结论:**在ArrayList的内部实现中,用于存储元素的数组( transient Object[] elementData; )被声明为transient,这意味着默认的序列化机制不会序列化这个数组。

60秒标准回答:

在ArrayList的内部实现中,用于存储元素的数组( transient Object[] elementData; )被声明为transient,这意味着默认的序列化机制不会序列化这个数组

那么,ArrayList是如何实现序列化的呢?

ArrayList自定义了序列化过程,通过重写writeObject和readObject方法

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**那么,ArrayList是如何实现序列化的呢?
  • **要点2:**ArrayList自定义了序列化过程,通过重写writeObject和readObject方法。
  • **要点3:**之所以这么做,ArrayList的数组通常会有一些空的位置(因为容量会预留一些空间),为了节省空间和提高效率,ArrayList没有使用默认的序列化机制,而是自定义了序列化,只序列化实际存储的元素,而不是整个数组。
  • **要点4:**ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那就会序列化99个null元素。
  • **要点5:**所以,为了避免Java自带的序列化机制造成的空间浪费,把数组定义为transient,然后重写writeObject和readObject来实现序列化操作。

**记忆锚点:**ArrayList → transient → writeObject → readObject → null → elementData

易错提醒:

  • 所以,为了避免Java自带的序列化机制造成的空间浪费,把数组定义为transient,然后重写writeObject和readObject来实现序列化操作。

加分表达:

  • ArrayList重写了writeObject和readObject方法,如下所示: 为什么底层数组要使用transient ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那就会序列化99个null元素。

追问准备:

  • 围绕「ArrayList」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「transient」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「writeObject」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

在ArrayList的内部实现中,用于存储元素的数组(**transient Object[] elementData;**)被声明为transient,这意味着默认的序列化机制不会序列化这个数组。

那么,ArrayList是如何实现序列化的呢?

ArrayList自定义了序列化过程,通过重写writeObject和readObject方法。

之所以这么做,ArrayList的数组通常会有一些空的位置(因为容量会预留一些空间),为了节省空间和提高效率,ArrayList没有使用默认的序列化机制,而是自定义了序列化,只序列化实际存储的元素,而不是整个数组。

ArrayList重写了writeObject和readObject方法,如下所示:

java
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}

知识扩展 ​

为什么底层数组要使用transient ​

ArrayList实际上是动态数组,每次在放满以后自动增长设定的长度值,如果数组自动增长长度设为100,而实际只放了一个元素,那就会序列化99个null元素。为了保证在序列化的时候不会将这么多null同时进行序列化,ArrayList把元素数组设置为transient。

所以,为了避免Java自带的序列化机制造成的空间浪费,把数组定义为transient,然后重写writeObject和readObject来实现序列化操作。