序列化Serializable和Parcelable

概念

Java中的序列化是一種將對象持久化(比如存儲在磁盤)的手段鼻种。一般情況下椎木,程序運行(即JVM運行)時镰绎,Java對象(短暫)存儲在內(nèi)存中缘揪。但JVM停止運行后耍群,對象的狀態(tài)信息就不能保存在內(nèi)存了。我們需要將對象持久化保存找筝,這就是序列化的用途蹈垢。
簡單說,序列化就是呻征,將對象的狀態(tài)信息轉(zhuǎn)換為字節(jié)序列的過程耘婚。通過序列化,可以將對象的狀態(tài)信息轉(zhuǎn)換為字節(jié)序列陆赋,然后通過IO流保存到磁盤或者網(wǎng)絡(luò)傳輸。然后從磁盤或網(wǎng)絡(luò)請求獲取到字節(jié)流嚷闭,通過反序列化攒岛,重新創(chuàng)建該對象。

Serializable

Serializable是Java提供的序列化接口胞锰,實現(xiàn)Serializable接口的類灾锯,極為可序列化的類⌒衢牛可以直接通過ObjectOutputStream序列化顺饮,也可以通過ObjectInputStream反序列化吵聪。

以下便是Java序列化的典型用法

public class SerializableTest implements Serializable {

    private final static long serialVersionUID = 1L;
    private String name;
    private String job;
    private UnSerializable unSerializable;

    public SerializableTest(String name, String job,UnSerializable unSerializable) {
        this.name = name;
        this.job = job;
        this.unSerializable = unSerializable;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public UnSerializable getUnSerializable() {
        return unSerializable;
    }

    public void setUnSerializable(UnSerializable unSerializable) {
        this.unSerializable = unSerializable;
    }
}
serialVersionUID

serialVersionUID是類的序列化ID,可以理解為這個類的版本號兼雄。serialVersionUID可以自己手動聲明吟逝,也可以由系統(tǒng)默認。系統(tǒng)默認的serialVersionUID是根據(jù)類的定義計算出一個serialVersionUID赦肋。如果類的定義發(fā)生了改變块攒,默認的serialVersionUID也會隨之變化,就像類的版本發(fā)生了改變一樣佃乘。
serialVersionUID的作用在于囱井,虛擬機是否允許反序列化,不僅僅是取決于類的路徑和功能代碼是否一致趣避。還有一個非常重要的判斷依據(jù)是庞呕,兩個類的serialVersionUID是否一致。

具體的場景就是:客戶端A和B通過網(wǎng)絡(luò)傳遞對象數(shù)據(jù)程帕,客戶端A將c對象序列化為字節(jié)流住练,通過網(wǎng)絡(luò)傳輸給客戶端B,然后B反序列化得到c骆捧。此時如果A和B端的app版本不同枪汪,而導致C類的serialVersionUID不同,就會導致B端反序列化失敗撒穷。
當然恋沃,有時候,我們也可以通過這種方法枫攀,強制app的低版本升級

Java官方是建議我們手動聲明serialVersionUID括饶。一般情況下沒有特殊需求,serialVersionUID就直接聲明為1L来涨。

成員變量序列化

一個對象的成員變量是不可序列化的對象的引用图焰,會導致對象序列化失敗,拋出NotSerializableException異常蹦掐。序列化要求技羔,對象的成員變量也是可序列化的。

靜態(tài)變量序列化

序列化保存的是對象的狀態(tài)信息卧抗,靜態(tài)變量屬于類的狀態(tài)藤滥,因此,序列化不保存靜態(tài)變量社裆。

父類的序列化

一個子類實現(xiàn)了Serializable接口拙绊,它的父類沒有實現(xiàn)Serializable接口。序列化該子類對象,然后反序列化輸出其父類定義的變量的值标沪,該變量的值和序列化之前不同榄攀。
如果父類沒有實現(xiàn)Serializable接口,虛擬機是不會序列化父類對象的金句。此時就要求檩赢,父類必須有無參構(gòu)造函數(shù)。而一個Java對象的構(gòu)造必須先有父類對象趴梢,再創(chuàng)建子類對象漠畜,反序列化時也不例外。所以反序列化時坞靶,虛擬機只能調(diào)用父類的無參構(gòu)造函數(shù)來創(chuàng)建父類對象憔狞。這樣,父類定義的成員變量的值彰阴,就是調(diào)用無參構(gòu)造函數(shù)后的值瘾敢。如果你考慮到了這種序列化的情況,就可以在父類的無參構(gòu)造函數(shù)對成員變量賦值尿这。

Transient

Transient關(guān)鍵字的作用就是控制變量的序列化簇抵,在變量聲明前聲明Transient,表示該變量不會被序列化射众。在反序列化時碟摆,該變量值就會是該類型的初始值,如int型為0叨橱,String型為null典蜕。

自定義readObject和WriteObject

將對象序列化后,進行網(wǎng)絡(luò)傳輸罗洗,就要考慮到數(shù)據(jù)的安全性問題愉舔。這樣的場景,可以考慮封裝自定義的writeObject和readObject方法伙菜。這樣可以序列化時轩缤,對數(shù)據(jù)進行加密,反序列化時解密贩绕。

public class SerializableTest implements Serializable {
    private final static long serialVersionUID = 1L;
    private String name;
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    private void writeObject(ObjectOutputStream out) {
        try {
            ObjectOutputStream.PutField putFields = out.putFields();
            System.out.println("原name:" + name);
            name = "encryption-"+name;//模擬加密
            putFields.put("name", name);
            System.out.println("加密后的name" + name);
            out.writeFields();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

      private void readObject(ObjectInputStream in) {
        try {
            ObjectInputStream.GetField readFields = in.readFields();
            Object object = readFields.get("name", "");
            System.out.println("要解密的字符串:" + object.toString());
            name = object.toString().split("-")[1];//模擬解密
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
序列化存儲規(guī)則

Java序列化機制為了節(jié)省磁盤空間火的,具有特定的存儲規(guī)則,當多次序列化寫入文件的是同一對象時淑倾,并不會再將對象的內(nèi)容寫入文件卫玖,而只是再存儲一個引用,此時寫入文件的就是新增的引用和一些控制信息踊淳。反序列化時,恢復引用關(guān)系,使得所有的引用指向的是同一個對象迂尝。這樣的特點脱茉,會帶來另一個值得注意的問題:

File file = new File("H:/sourceCode/workspace4java/test.txt");
            SerializableTest testObject = new SerializableTest("Smith","engineer",32,new UnSerializable("1024"));
            FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(testObject);
            oos.flush();
            testObject.setName("James");
            oos.writeObject(testObject);
            oos.close();
            FileInputStream fis = new FileInputStream(file);
            ObjectInputStream ois = new ObjectInputStream(fis);
            SerializableTest result1 = (SerializableTest)ois.readObject();
            SerializableTest result2 = (SerializableTest)ois.readObject();
            System.out.println(result1.getName());
            System.out.println(result2.getName());

輸出結(jié)果是

Smith
Smith

我們希望看到的是第二次反序列化的對象name為James,但是可以看到垄开,兩次反序列化的對象的name一樣琴许。這是因為第二次序列化寫入時,虛擬機根據(jù)引用關(guān)系溉躲,已經(jīng)判斷出寫入的是同一對象榜田,因此只保存了第二次的引用。所以反序列化讀取時锻梳,都是第一次保存的對象箭券。

Parcelable

Parcelable是Android提供的序列化接口。它相比于Serializable的優(yōu)點是疑枯,序列化的效率要高一些辩块。 實現(xiàn)了 Parcelable 接口的類在序列化和反序列化時會被轉(zhuǎn)換為 Parcel 類型的數(shù)據(jù) 。 Parcel 是一個載體荆永,它可以包含數(shù)據(jù)或者對象引用废亭,然后通過 IBinder 在進程間傳遞。
Parcelable和Serializable的對比:

  • 一般在保存數(shù)據(jù)到 SD 卡或者網(wǎng)絡(luò)傳輸時建議使用 Serializable 即可具钥,雖然效率差一些豆村,好在使用方便。
  • 而在運行時數(shù)據(jù)傳遞時建議使用 Parcelable骂删,比如 Intent掌动,Bundle 等,Android 底層做了優(yōu)化處理桃漾,效率很高坏匪。

本文參考:
[https://www.ibm.com/developerworks/cn/java/j-lo-serial/index.html
https://www.hollischuang.com/archives/1140
https://blog.csdn.net/u011240877/article/details/72455715

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撬统,隨后出現(xiàn)的幾起案子适滓,更是在濱河造成了極大的恐慌,老刑警劉巖恋追,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凭迹,死亡現(xiàn)場離奇詭異,居然都是意外死亡苦囱,警方通過查閱死者的電腦和手機嗅绸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來撕彤,“玉大人鱼鸠,你說我怎么就攤上這事猛拴。” “怎么了蚀狰?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵愉昆,是天一觀的道長。 經(jīng)常有香客問我麻蹋,道長跛溉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任扮授,我火速辦了婚禮芳室,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘刹勃。我一直安慰自己堪侯,他們只是感情好,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布深夯。 她就那樣靜靜地躺著抖格,像睡著了一般。 火紅的嫁衣襯著肌膚如雪咕晋。 梳的紋絲不亂的頭發(fā)上雹拄,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天,我揣著相機與錄音掌呜,去河邊找鬼滓玖。 笑死,一個胖子當著我的面吹牛质蕉,可吹牛的內(nèi)容都是我干的势篡。 我是一名探鬼主播,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼模暗,長吁一口氣:“原來是場噩夢啊……” “哼禁悠!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起兑宇,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤碍侦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后隶糕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓷产,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年枚驻,在試婚紗的時候發(fā)現(xiàn)自己被綠了濒旦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡再登,死狀恐怖尔邓,靈堂內(nèi)的尸體忽然破棺而出晾剖,到底是詐尸還是另有隱情,我是刑警寧澤铃拇,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布钞瀑,位于F島的核電站,受9級特大地震影響慷荔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缠俺,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一显晶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壹士,春花似錦磷雇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至盒使,卻和暖如春崩掘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背少办。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工苞慢, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人英妓。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓挽放,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蔓纠。 傳聞我的和親對象是個殘疾皇子辑畦,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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