ArrayList中elementData为何被transient修饰?

  Java的ArrayList中,定义了一个数组elementData用来装载对象的,具体定义以下:java

/**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
 * will be expanded to DEFAULT_CAPACITY when the first element is added.
 */
transient Object[] elementData; // non-private to simplify nested class access

transient用来表示一个域不是该对象序行化的一部分,当一个对象被序行化的时候,transient修饰的变量的值是不包括在序行化的表示中的。可是ArrayList又是可序行化的类,elementData是ArrayList具体存放元素的成员,用transient来修饰elementData,岂不是反序列化后的ArrayList丢失了原先的元素?
       其实玄机在于ArrayList中的两个方法:数组

/**
 * Save the state of the <tt>ArrayList</tt> instance to a stream (that
 * is, serialize it).
 *
 * @serialData The length of the array backing the <tt>ArrayList</tt>
 *             instance is emitted (int), followed by all of its elements
 *             (each an <tt>Object</tt>) in the proper order.
 */
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();
    }
}

 

/**
 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
 * deserialize it).
 */
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();
        }
    }
}

       ArrayList在序列化的时候会调用writeObject,直接将size和element写入ObjectOutputStream;反序列化时调用readObject,从ObjectInputStream获取size和element,再恢复到elementData。
       为何不直接用elementData来序列化,而采用上诉的方式来实现序列化呢?缘由在于elementData是一个缓存数组,它一般会预留一些容量,等容量不足时再扩充容量,那么有些空间可能就没有实际存储元素,采用上诉的方式来实现序列化时,就能够保证只序列化实际存储的那些元素,而不是整个数组,从而节省空间和时间。缓存

 

transient分析
注意到的是ArrayList和LinkedList中的一些变量被transient关键字修饰。如ArrayList中的elementData数组,LinkedList中指向头结点和尾结点的指针等。下面解释一下transient关键字的做用:this

java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我不想用serialization机制来保存它。为了在一个特定对象的域上关闭serialization,能够在这个域前加上关键字transient。transient是一个关键字,用来表示一个于不是该对象串行化的一部分。当一个对象被串行化的时候,被transient关键字修饰的变量的值不包括在串行化的表示中,非transient型的变量是被包括进去的。指针

那么既然用于保存数据的变量都被transient修饰,ArrayList和LinkedList还能不能被序列化呢?code

答案是能够的。对于ArrayList来讲,若是不把elementData申明为transient类型,那么序列化的时候里面的数据都会被序列化,可是elementData这个数组很大程序是存在空值的状况(即size《length),这时若是序列化就会致使磁盘空间被浪费。为了解决这个问题,ArrayList将elementData申明为transient,本身重写了writeObject方法,保证只序列化elementData中有数据的那部分,ArrayList中的writeObject方法以下:对象

(见上文)ci

注意的是这里面也用到了modCount,若是不一致说明这段时间集合发生了改变,抛出异常。element

LinkedList中的序列化和ArrayList中的序列化还不同,LinkedList中不会存在说有多余的元素这种说法,可是因为LinkedList中申明为transient类型的数据均可以不用进行序列化,因此进行申明,好比分别指向头结点和尾结点的first和last指针。it

相关文章
相关标签/搜索