ArrayList的反序列化腊尚,transient Object[] elementData怎么辦

ArrayList的成員變量聲明如下:

private transient Object[] elementData;// 存儲(chǔ)ArrayList中的元素

這里會(huì)有幾個(gè)問題:

  1. 為什么實(shí)現(xiàn)了Serializable接口卻不想序列化這個(gè)屬性。
  2. 最重要的存儲(chǔ)數(shù)據(jù)的地方箕慧,不序列化這些元素值怎么辦?

針對(duì)第一個(gè)問題:
因?yàn)閑lementData數(shù)組的大小實(shí)際上是大于等于實(shí)際存儲(chǔ)元素個(gè)數(shù)的茴恰,如果直接采用默認(rèn)的實(shí)例化的話颠焦,會(huì)將很多null序列化,這沒必要琐簇。

針對(duì)第二個(gè)問題:
基于第一個(gè)問題蒸健,所以ArrayList中有一個(gè)private void writeObject的私有方法座享。

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);

    // 序列化實(shí)際存儲(chǔ)的元素
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

至此引出一個(gè)問題:ArrayList的序列化到底是一個(gè)怎樣的過程?

ArrayList的序列化

編寫一個(gè)測(cè)試代碼:

@Test
public void testSerializable() throws Exception {
    ArrayList<String> list = new ArrayList<>();
    list.add("string1");
    list.add("string2");

    // 序列化
    File file = new File("d:/testSerializable");
    FileOutputStream fs = new FileOutputStream(file);
    ObjectOutputStream oos = new ObjectOutputStream(fs);
    oos.writeObject(list);
    fs.close();
    oos.close();

    // 反序列化
    FileInputStream fi = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(fi);
    ArrayList<String> readObject = (ArrayList) ois.readObject();
    System.out.println(readObject);// 輸出[string1, string2]似忧,說明元素被序列化了
    fi.close();
    ois.close();
}

跟蹤源碼:

public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {// 用于為false渣叛,子類可以設(shè)置成true
        writeObjectOverride(obj);
        return;
    }
    try {
        // 真正的邏輯
        writeObject0(obj, false);
    } catch (IOException ex) {
        // 省略
    }
}

private void writeObject0(Object obj, boolean unshared)
    throws IOException
{
    // 省略
    try {
        // 省略

        // check for replacement object
        Object orig = obj;
        Class<?> cl = obj.getClass();
        // 類序列化的描述符,里面包含了obj對(duì)應(yīng)class的一些屬性盯捌,如:是否有writeObject方法
        ObjectStreamClass desc;
        for (;;) {
            // REMIND: skip this check for strings/arrays?
            Class<?> repCl;
            // 解析cl淳衙,獲取實(shí)例
            desc = ObjectStreamClass.lookup(cl, true);
            if (!desc.hasWriteReplaceMethod() ||
                (obj = desc.invokeWriteReplace(obj)) == null ||
                (repCl = obj.getClass()) == cl)
            {
                break;
            }
            cl = repCl;
        }
        // 省略

        // remaining cases
        if (obj instanceof String) {
            writeString((String) obj, unshared);
        } else if (cl.isArray()) {
            writeArray(obj, desc, unshared);
        } else if (obj instanceof Enum) {
            writeEnum((Enum<?>) obj, desc, unshared);
        } else if (obj instanceof Serializable) {
            // 實(shí)現(xiàn)了Serializable接口
            writeOrdinaryObject(obj, desc, unshared);
        } else {
            if (extendedDebugInfo) {
                throw new NotSerializableException(
                    cl.getName() + "\n" + debugInfoStack.toString());
            } else {
                throw new NotSerializableException(cl.getName());
            }
        }
    } finally {
        // 省略
    }
}

// 調(diào)用writeOrdinaryObject -> writeSerialData
private void writeSerialData(Object obj, ObjectStreamClass desc)
    throws IOException
{
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots[i].desc;
        // 從類序列化描述符ObjectStreamClass獲取obj對(duì)應(yīng)的類,是否有writeObject方法
        if (slotDesc.hasWriteObjectMethod()) {
            PutFieldImpl oldPut = curPut;
            curPut = null;
            SerialCallbackContext oldContext = curContext;

            if (extendedDebugInfo) {
                debugInfoStack.push(
                    "custom writeObject data (class \"" +
                    slotDesc.getName() + "\")");
            }
            try {
                curContext = new SerialCallbackContext(obj, slotDesc);
                bout.setBlockDataMode(true);
                // 通過反射調(diào)用writeObject方法饺著,對(duì)應(yīng)ArrayList#writeObject
                slotDesc.invokeWriteObject(obj, this);
                bout.setBlockDataMode(false);
                bout.writeByte(TC_ENDBLOCKDATA);
            } finally {
                
            }

            curPut = oldPut;
        } else {
            // 序列化非transient屬性
            defaultWriteFields(obj, slotDesc);
        }
    }
}

總結(jié)

一切都比較明了箫攀,ArrayList中有writeObject方法是為了保證只序列化真實(shí)的元素,通過序列化時(shí)獲取的ObjectStreamClass實(shí)例幼衰,如果需要反序列化的類(如ArrayList)包含writeObject方法靴跛,就通過反射調(diào)用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渡嚣,一起剝皮案震驚了整個(gè)濱河市梢睛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌识椰,老刑警劉巖绝葡,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異腹鹉,居然都是意外死亡藏畅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門功咒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愉阎,“玉大人,你說我怎么就攤上這事航瞭〗胨叮” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵刊侯,是天一觀的道長(zhǎng)章办。 經(jīng)常有香客問我,道長(zhǎng)滨彻,這世上最難降的妖魔是什么藕届? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮亭饵,結(jié)果婚禮上休偶,老公的妹妹穿的比我還像新娘。我一直安慰自己辜羊,他們只是感情好踏兜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布词顾。 她就那樣靜靜地躺著,像睡著了一般碱妆。 火紅的嫁衣襯著肌膚如雪肉盹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天疹尾,我揣著相機(jī)與錄音上忍,去河邊找鬼。 笑死纳本,一個(gè)胖子當(dāng)著我的面吹牛窍蓝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播繁成,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼吓笙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了朴艰?” 一聲冷哼從身側(cè)響起观蓄,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎祠墅,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歌径,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡毁嗦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了回铛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狗准。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖茵肃,靈堂內(nèi)的尸體忽然破棺而出腔长,到底是詐尸還是另有隱情,我是刑警寧澤验残,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布捞附,位于F島的核電站,受9級(jí)特大地震影響您没,放射性物質(zhì)發(fā)生泄漏鸟召。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一氨鹏、第九天 我趴在偏房一處隱蔽的房頂上張望欧募。 院中可真熱鬧,春花似錦仆抵、人聲如沸跟继。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)舔糖。三九已至碌廓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間剩盒,已是汗流浹背谷婆。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辽聊,地道東北人纪挎。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跟匆,于是被迫代替她去往敵國(guó)和親异袄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 文章1:https://www.hollischuang.com/archives/1150文章2:http://...
    zjkdifvpwlkqumn閱讀 192評(píng)論 0 0
  • 在Java中玛臂,我們可以通過多種方式來創(chuàng)建對(duì)象烤蜕,并且只要對(duì)象沒有被回收我們都可以復(fù)用該對(duì)象。但是迹冤,我們創(chuàng)建出來的這些...
    懶癌正患者閱讀 1,534評(píng)論 0 12
  • 5月以來莉兰,哪怕對(duì)市場(chǎng)風(fēng)向再不敏感的人,也感覺到陣陣涼意礁竞。二級(jí)市場(chǎng)連續(xù)下挫糖荒,一級(jí)市場(chǎng)融資環(huán)境惡化,不論企業(yè)融資數(shù)量還...
    錢皓頻道閱讀 6,055評(píng)論 1 6
  • 推薦指數(shù): 6.0 書籍主旨關(guān)鍵詞:特權(quán)模捂、焦點(diǎn)捶朵、注意力、語言聯(lián)想枫绅、情景聯(lián)想 觀點(diǎn): 1.統(tǒng)計(jì)學(xué)現(xiàn)在叫數(shù)據(jù)分析泉孩,社會(huì)...
    Jenaral閱讀 5,721評(píng)論 0 5
  • 昨天,在回家的路上并淋,坐在車?yán)镉圃沼圃盏乜粗摹度龉衬墓适隆吩幔冶焕锩娴膬?nèi)容深深吸引住了,盡管上學(xué)時(shí)...
    夜闌曉語閱讀 3,788評(píng)論 2 9