ArrayList和LinkedList中的transient關(guān)鍵字和序列化

一、準備

在看ArrayList和LinkedList的 源碼 的時候蹋笼,發(fā)現(xiàn)ArrayList和LinkedList的一些成員變量變量被transient修飾了喂走,有點不解躬存,就查了一些資料。分享給大家息裸。

ArrayList源碼中的transient

LinkedList中的關(guān)鍵字

1.1 序列化與反序列化

序列化是java提供的一種將內(nèi)存中的對象信息轉(zhuǎn)化為二進制數(shù)組的方法蝇更,可以將數(shù)組保存和傳輸,然后使用原來的類模板恢復(fù)對象的信息呼盆。
轉(zhuǎn)化后的二進制數(shù)組中包含以下信息:序列化版本年扩,完整類名,serialVersionUID访圃,各個屬性的類型厨幻、名字和值、父類信息腿时。

1.2 怎么實現(xiàn)序列化和反序列化

實現(xiàn)Serializable接口况脆,使用ObjectOutputStream.writeObject(Object Object)寫對象信息,使用ObjectInputStream.readObject()讀對象信息批糟。

package Aug_05;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SerializebleTest implements Serializable{
    int num = 10;
    private static final long serialVersionUID = -1L;  //設(shè)置好serialVersionUID格了,減少反序列化失敗的機率
    
    public static void main(String[] args) {
        try {
            //將對象序列化,并保存到本地文件中
            FileOutputStream fileOutputStream = new FileOutputStream("MySerialize");  //創(chuàng)建文件輸出流
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);  //創(chuàng)建對象輸出流
            SerializebleTest oldObject = new SerializebleTest();
            objectOutputStream.writeObject(oldObject);  //寫對象信息
            objectOutputStream.flush();
            objectOutputStream.close();
            fileOutputStream.close();
            //從本地文件中讀取序列化信息徽鼎,恢復(fù)對象
            FileInputStream fileInputStream = new FileInputStream("MySerialize");
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
            SerializebleTest newObject = (SerializebleTest)objectInputStream.readObject();
            objectInputStream.close();
            fileInputStream.close();
            System.out.println("原始對象和恢復(fù)的對象是否是同一個對象:" + (newObject == oldObject));
            System.out.println("原始對象和恢復(fù)的對象中的值是否相同:" + (newObject.num == oldObject.num));
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

ObjectOutputStream.writeObject(ObjectA objectA)首先判斷ObjectA有沒有重寫writeObject方法盛末,如果有,反射調(diào)用ObjectA的writeObject方法完成序列化纬傲;否則满败,調(diào)用默認的序列化方法序列化。反序列化也一樣


ObjectOutputStream的writeObject方法

1.2 transient關(guān)鍵字

被transient修飾的成員變量不會被序列化叹括。
哪些情況下可以不用序列化呢算墨?我認為有以下兩種情況。
1.節(jié)省空間
比如汁雷,一個長方形類有成員變量:長净嘀、寬、面積侠讯。那么面積就不用序列化挖藏,因為面積可以根據(jù)其他兩個計算出來,這樣節(jié)省存儲空間和傳輸空間厢漩。
2.持有對象的引用
比如膜眠,我們創(chuàng)建鏈表的結(jié)點如下。結(jié)點中持有前驅(qū)結(jié)點和后繼結(jié)點的引用,引用就是對象在內(nèi)存中的地址值宵膨。對于這樣的結(jié)點形成的鏈表架谎,我們序列化這個鏈表后,結(jié)點的前序和后繼引用都失效了辟躏,因為內(nèi)存地址變了谷扣。這種情況下我們需要重新連接鏈表。

    private static class Node<E> {
        E item;   // 數(shù)據(jù)
        Node<E> next;  //后繼   
        Node<E> prev;  //前驅(qū)

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }

二捎琐、分析

首先說以下結(jié)論:
1.ArrayList中將elementData修飾成transient是為了節(jié)省空間
2.LinkedList中將first和last修飾成transient是為了節(jié)省空間和重新連接鏈表会涎。

2.1 ArrayList分析

查看 源碼 我們知道ArrayList中使用數(shù)組transient Object[] elementData保存數(shù)據(jù),當數(shù)組空間不夠時瑞凑,數(shù)組長度擴容為原來的1.5倍末秃。那么數(shù)組中可能有沒有使用的空間,比如elementData的長度時15拨黔,但是里面只裝了11個元素蛔溃,那么后面的4個元素都是空值。序列化的時候可以不把這4個元素序列化篱蝇。
ArrayList中定義了writeObject和readObject方法贺待,實現(xiàn)了自定義序列化。前面我們說了序列化的時候ObjectStream會判斷類中有沒有自定義序列化方法零截?如果有麸塞,使用自定義序列化方法:否則使用默認的序列化方法。
ArrayList自定義序列化方法如下

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // 先調(diào)用默認的序列化方法涧衙,將沒有transient修飾的成員變量序列化
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // 將元素容量序列化
        s.writeInt(size);

        // 將不為空的元素序列化
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

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

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

        // 先調(diào)用默認的反序列化方法
        s.defaultReadObject();

        // 反序列化元素容量
        s.readInt(); // ignored

        if (size > 0) {
            // 檢查容量哪工,不過不夠進行擴容
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // 反序列化元素
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

2.2 LinkedList分析

查看 源碼 我們知道LinkedList中使用雙向鏈表保存數(shù)據(jù),結(jié)點中保存前驅(qū)和后繼的引用弧哎。但是序列化之后前序結(jié)點和后繼結(jié)點的地址都變了雁比,我們應(yīng)該連接新的結(jié)點。
下面看以下LinkedList是怎么自定義序列化的

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // 先調(diào)用默認的序列化方法
        s.defaultWriteObject();

        // 序列化容量
        s.writeInt(size);

        // 只把結(jié)點中的值序列化撤嫩,前序和后繼的引用不序列化
        for (Node<E> x = first; x != null; x = x.next)
            s.writeObject(x.item);
    }


    @SuppressWarnings("unchecked")
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // 先調(diào)用默認的反序列化方法
        s.defaultReadObject();

        // 讀容量
        int size = s.readInt();

        // 讀取每一個結(jié)點保存的值偎捎,創(chuàng)建新結(jié)點,重新連接鏈表序攘。
        for (int i = 0; i < size; i++)
            linkLast((E)s.readObject());
    }

我們看到了茴她,LinkedList序列化的時候?qū)㈡湵戆错樞虿鸱珠_來,僅序列化結(jié)點中保存的數(shù)據(jù)程奠,反序列化的時候重新連接鏈表丈牢,保證了鏈表的有效性。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瞄沙,一起剝皮案震驚了整個濱河市己沛,隨后出現(xiàn)的幾起案子慌核,更是在濱河造成了極大的恐慌,老刑警劉巖申尼,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件遂铡,死亡現(xiàn)場離奇詭異,居然都是意外死亡晶姊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門伪货,熙熙樓的掌柜王于貴愁眉苦臉地迎上來们衙,“玉大人,你說我怎么就攤上這事碱呼∶商簦” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵愚臀,是天一觀的道長忆蚀。 經(jīng)常有香客問我,道長姑裂,這世上最難降的妖魔是什么馋袜? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮舶斧,結(jié)果婚禮上欣鳖,老公的妹妹穿的比我還像新娘。我一直安慰自己茴厉,他們只是感情好泽台,可當我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著矾缓,像睡著了一般怀酷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嗜闻,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天蜕依,我揣著相機與錄音,去河邊找鬼泞辐。 笑死笔横,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的咐吼。 我是一名探鬼主播吹缔,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锯茄!你這毒婦竟也來了厢塘?” 一聲冷哼從身側(cè)響起茶没,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晚碾,沒想到半個月后抓半,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡格嘁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年笛求,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糕簿。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡探入,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出懂诗,到底是詐尸還是另有隱情蜂嗽,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布殃恒,位于F島的核電站植旧,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏离唐。R本人自食惡果不足惜病附,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望亥鬓。 院中可真熱鬧胖喳,春花似錦、人聲如沸贮竟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咕别。三九已至技健,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惰拱,已是汗流浹背雌贱。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留偿短,地道東北人欣孤。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像昔逗,于是被迫代替她去往敵國和親降传。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,884評論 2 354