1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        序列化與反序列化——作為Java開(kāi)發(fā),應(yīng)該避開(kāi)這些坑

        共 8150字,需瀏覽 17分鐘

         ·

        2021-12-17 15:55

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        1.序列化與反序列化的概念

        先說(shuō)說(shuō)序列化和反序列化的概念

        序列化:將對(duì)象寫(xiě)入到IO流中
        反序列化:從
        IO流中恢復(fù)對(duì)象

        Serializable接口是一個(gè)標(biāo)記接口,不用實(shí)現(xiàn)任何方法,標(biāo)記當(dāng)前類(lèi)對(duì)象是可以序列化的,是給JVM看的。

        ??序列化機(jī)制允許將這些實(shí)現(xiàn)序列化接口的對(duì)象轉(zhuǎn)化為字節(jié)序列,這些字節(jié)序列可以保證在磁盤(pán)上或者網(wǎng)絡(luò)傳輸后恢復(fù)成原來(lái)的對(duì)象。序列化就是把對(duì)象存儲(chǔ)在JVM以外的地方,序列化機(jī)制可以讓對(duì)象脫離程序的運(yùn)行而獨(dú)立存在。

        序列化在業(yè)務(wù)代碼也許用的不多,但是在框架層面用的是很多的。

        相關(guān)技術(shù):Session的序列化或者反序列化

        先給出序列化的例子,請(qǐng)記住這個(gè)People類(lèi),后面會(huì)根據(jù)這個(gè)類(lèi)來(lái)改造講解。

        public?class?People?{

        ????private?Long?id;

        ????public?People(Long?id)?{
        ????????this.id?=?id;
        ????}

        ????public?Long?getId()?{
        ????????return?id;
        ????}

        ????public?void?setId(Long?id)?{
        ????????this.id?=?id;
        ????}

        ????@Override
        ????public?String?toString()?{
        ????????return?"People{"?+
        ????????????????"id="?+?id?+
        ????????????????'}';
        ????}
        }


        import?java.io.*;

        //?屏蔽編譯器的警告
        @SuppressWarnings("all")
        public?class?Main?{

        ????/**
        ?????*?

        序列化和反序列化?People?對(duì)象


        ?????*/
        ????private?static?void?testSerializablePeople()?throws?Exception?{

        ????????//?序列化的步驟

        ????????//?用于存儲(chǔ)序列化的文件,這里的java_下劃線(xiàn)僅僅為了說(shuō)明是java序列化對(duì)象,沒(méi)有任何其他含義
        ????????File?file?=?new?File("/tmp/people_10.java_");
        ????????if?(!file.exists())?{
        ????????????//?1,先得到文件的上級(jí)目錄,并創(chuàng)建上級(jí)目錄
        ????????????file.getParentFile().mkdirs();
        ????????????try?{
        ????????????????//?2,再創(chuàng)建文件
        ????????????????file.createNewFile();
        ????????????}?catch?(IOException?e)?{
        ????????????????e.printStackTrace();
        ????????????}
        ????????}
        ????????People?p?=?new?People(10L);

        ????????//?創(chuàng)建一個(gè)輸出流
        ????????ObjectOutputStream?oos?=?new?ObjectOutputStream(
        ????????????????new?FileOutputStream(file)
        ????????);
        ????????//?輸出可序列化對(duì)象
        ????????oos.writeObject(p);
        ????????//?關(guān)閉輸出流
        ????????oos.close();

        ????????//?反序列化的步驟

        ????????//?創(chuàng)建一個(gè)輸入流
        ????????ObjectInputStream?ois?=?new?ObjectInputStream(
        ????????????????new?FileInputStream(file)
        ????????);
        ????????//?得到反序列化的對(duì)象,這里可以強(qiáng)轉(zhuǎn)為People類(lèi)型
        ????????Object?newPerson?=?ois.readObject();
        ????????//?關(guān)閉輸入流
        ????????ois.close();

        ????????System.out.println(newPerson);
        ????}

        ????public?static?void?main(String[]?args)?throws?Exception?{
        ????????testSerializablePeople();
        ????}
        }

        運(yùn)行之后,看到磁盤(pán)文件因?yàn)樾蛄谢嗔艘粋€(gè)文件

        控制臺(tái)中因反序列化輸出的對(duì)象信息打印如下:

        2.子類(lèi)實(shí)現(xiàn)Serializable接口,父類(lèi)沒(méi)有實(shí)現(xiàn),子類(lèi)可以序列化嗎?

        去掉父類(lèi)Peopleimplements Serializable,讓父類(lèi)不實(shí)現(xiàn)序列化接口,子類(lèi)Worker實(shí)現(xiàn)序列化接口

        public?class?Worker?extends?People?implements?Serializable?{

        ????private?String?name;
        ????private?Integer?age;

        ????public?Worker(Long?id,?String?name,?Integer?age)?{
        ????????super(id);
        ????????this.name?=?name;
        ????????this.age?=?age;
        ????}

        }


        ?public?static?void?main(String[]?args)?throws?Exception?{
        ????????testSerizableWorker();
        ????}

        ????/**
        ?????*?

        子類(lèi)實(shí)現(xiàn)序列化,?父類(lèi)不實(shí)現(xiàn)序列化


        ?????*?*/
        ????private?static?void?testSerizableWorker()?throws?Exception?{

        ????????File?file?=?new?File("/tmp/worker_10.java_");
        ????????if?(!file.exists())?{
        ????????????//?1,先得到文件的上級(jí)目錄,并創(chuàng)建上級(jí)目錄
        ????????????file.getParentFile().mkdirs();
        ????????????try?{
        ????????????????//?2,再創(chuàng)建文件
        ????????????????file.createNewFile();
        ????????????}?catch?(IOException?e)?{
        ????????????????e.printStackTrace();
        ????????????}
        ????????}
        ????????Worker?p?=?new?Worker(10L,?"lcy",?18);

        ????????//?創(chuàng)建一個(gè)輸出流
        ????????ObjectOutputStream?oos?=?new?ObjectOutputStream(
        ????????????????new?FileOutputStream(file)
        ????????);
        ????????//?輸出可序列化對(duì)象
        ????????oos.writeObject(p);
        ????????//?關(guān)閉輸出流
        ????????oos.close();

        ????????ObjectInputStream?ois?=?new?ObjectInputStream(new?FileInputStream(file));
        ????????Object?newWorker?=?ois.readObject();?//?父類(lèi)沒(méi)有序列化的時(shí)候,需要調(diào)用父類(lèi)的無(wú)參數(shù)構(gòu)造方法
        ????????ois.close();
        ????????System.out.println(newWorker);
        ????}

        再次測(cè)試運(yùn)行

        ??結(jié)果顯示沒(méi)有有效地構(gòu)造器,原來(lái)是因?yàn)楦割?lèi)沒(méi)有序列化的時(shí)候,Object newWorker = ois.readObject()需要直接調(diào)用父類(lèi)的無(wú)參數(shù)構(gòu)造方法,不經(jīng)過(guò)子類(lèi)的無(wú)參構(gòu)造方法。

        我們?cè)诟割?lèi)People中加上空的構(gòu)造方法之后再次執(zhí)行

        ??結(jié)果卻發(fā)現(xiàn)打印的不是Worker,而是父類(lèi)People,因?yàn)樽宇?lèi)沒(méi)有實(shí)現(xiàn)toString而調(diào)用父類(lèi)的toString,所以打印了People對(duì)象,至于父類(lèi)成員變量id為什么是null,原因如下:

        ??一個(gè)子類(lèi)實(shí)現(xiàn)了?Serializable?接口,它的父類(lèi)都沒(méi)有實(shí)現(xiàn)?Serializable接口,序列化該子類(lèi)對(duì)象。要想反序列化后輸出父類(lèi)定義的某變量的數(shù)值,就需要讓父類(lèi)也實(shí)現(xiàn)Serializable接口或者父類(lèi)有默認(rèn)的無(wú)參的構(gòu)造函數(shù)。

        ??在父類(lèi)沒(méi)有實(shí)現(xiàn)
        Serializable?接口時(shí),虛擬機(jī)是不會(huì)序列化父對(duì)象的,而一個(gè)?Java對(duì)象的構(gòu)造必須先有父對(duì)象,才有子對(duì)象,反序列化也不例外。所以反序列化時(shí),為了構(gòu)造父對(duì)象,只能調(diào)用父類(lèi)的無(wú)參構(gòu)造函數(shù)作為默認(rèn)的父對(duì)象。因此當(dāng)我們?nèi)「笇?duì)象的變量值時(shí),它的值是調(diào)用父類(lèi)無(wú)參構(gòu)造函數(shù)后的值,如果在父類(lèi)無(wú)參構(gòu)造函數(shù)中沒(méi)有對(duì)變量賦值,那么父類(lèi)成員變量值都是默認(rèn)值,如這里的Long型就是null

        ??根據(jù)以上特性,我們可以將不需要被序列化的字段抽取出來(lái)放到父類(lèi)中,子類(lèi)實(shí)現(xiàn)?
        Serializable接口,父類(lèi)不實(shí)現(xiàn)Serializable接口但提供一個(gè)空構(gòu)造方法,則父類(lèi)的字段數(shù)據(jù)將不被序列化。

        最后加上子類(lèi)WorkertoString方法,打印結(jié)果如下:

        總結(jié):
        子類(lèi)實(shí)現(xiàn)Serializable接口,父類(lèi)沒(méi)有實(shí)現(xiàn),子類(lèi)可以序列化??!
        這種情況父類(lèi)一定要提供空構(gòu)造方法,不要忘了子類(lèi)的toString方法!

        3.類(lèi)中存在引用對(duì)象,這個(gè)類(lèi)對(duì)象在什么情況下可以實(shí)現(xiàn)序列化?

        ??來(lái)一個(gè)組合對(duì)象,里面引用People對(duì)象,此時(shí)People對(duì)象沒(méi)有實(shí)現(xiàn)Serializable接口,能否序列化呢?代碼給出來(lái),大家可以自行復(fù)制測(cè)試一下。

        public?class?Combo?implements?Serializable?{

        ????private?int?id;
        ????private?People?people;

        ????public?Combo(int?id,?People?people)?{
        ????????this.id?=?id;
        ????????this.people?=?people;
        ????}

        ????public?int?getId()?{
        ????????return?id;
        ????}

        ????public?void?setId(int?id)?{
        ????????this.id?=?id;
        ????}

        ????public?People?getPeople()?{
        ????????return?people;
        ????}

        ????public?void?setPeople(People?people)?{
        ????????this.people?=?people;
        ????}
        ????
        ????@Override
        ????public?String?toString()?{
        ????????return?"Combo{"?+
        ????????????????"id="?+?id?+
        ????????????????",?people="?+?people?+
        ????????????????'}';
        ????}
        }


        public?class?People?{

        ????private?Long?id;

        ????public?People()?{
        ????}

        ????public?People(Long?id)?{
        ????????this.id?=?id;
        ????}

        ????public?Long?getId()?{
        ????????return?id;
        ????}

        ????public?void?setId(Long?id)?{
        ????????this.id?=?id;
        ????}

        ????@Override
        ????public?String?toString()?{
        ????????return?"People{"?+
        ????????????????"id="?+?id?+
        ????????????????'}';
        ????}
        }


        ????private?static?void?testSerializableCombo()?throws?Exception?{

        ????????File?file?=?new?File("/tmp/combo_10.java_");
        ????????if?(!file.exists())?{
        ????????????//?1,先得到文件的上級(jí)目錄,并創(chuàng)建上級(jí)目錄
        ????????????file.getParentFile().mkdirs();
        ????????????try?{
        ????????????????//?2,再創(chuàng)建文件
        ????????????????file.createNewFile();
        ????????????}?catch?(IOException?e)?{
        ????????????????e.printStackTrace();
        ????????????}
        ????????}
        ????????Combo?p?=?new?Combo(1,?new?People(10L));

        ????????//?創(chuàng)建一個(gè)輸出流
        ????????ObjectOutputStream?oos?=?new?ObjectOutputStream(
        ????????????????new?FileOutputStream(file)
        ????????);
        ????????//?輸出可序列化對(duì)象
        ????????oos.writeObject(p);
        ????????//?關(guān)閉輸出流
        ????????oos.close();

        ????????ObjectInputStream?ois?=?new?ObjectInputStream(new?FileInputStream(file));
        ????????Object?newCombo?=?ois.readObject();
        ????????ois.close();
        ????????System.out.println(newCombo);
        ????}

        ????public?static?void?main(String[]?args)?throws?Exception?{
        ????????testSerializableCombo();
        ????}


        運(yùn)行結(jié)果如下

        直接爆出異常,說(shuō)明People類(lèi)沒(méi)有序列化。
        當(dāng)
        People加上implements Serializable實(shí)現(xiàn)序列化接口后,再次執(zhí)行如下

        總結(jié):
        ??一個(gè)類(lèi)里面所有的屬性必須是可序列化的,這個(gè)類(lèi)才能順利的序列化。比如,類(lèi)中存在引用對(duì)象,那么這個(gè)引用對(duì)象必須是可序列化的,這個(gè)類(lèi)才能序列化。

        4.同一個(gè)對(duì)象多次序列化之間有屬性更新,前后的序列化有什么區(qū)別?

        ??下面例子中People是可序列化的,每次序列化之前都會(huì)把Peopleid值修改了,用來(lái)觀察看看,多次序列化期間,如果對(duì)象屬性更新,是否會(huì)影響序列化,反序列化有什么區(qū)別。

        ????/**
        ?????*?

        同一個(gè)對(duì)象多次序列化的問(wèn)題,?坑


        ?????*?*/
        ????private?static?void?sameObjectRepeatedSerialization()?throws?Exception?{

        ????????File?file?=?new?File("/tmp/peopele_more.java_");
        ????????if?(!file.exists())?{
        ????????????//?1,先得到文件的上級(jí)目錄,并創(chuàng)建上級(jí)目錄
        ????????????file.getParentFile().mkdirs();
        ????????????try?{
        ????????????????//?2,再創(chuàng)建文件
        ????????????????file.createNewFile();
        ????????????}?catch?(IOException?e)?{
        ????????????????e.printStackTrace();
        ????????????}
        ????????}
        ????????People?p?=?new?People(10L);
        ????????ObjectOutputStream?oos?=?new?ObjectOutputStream(new?FileOutputStream(file));
        ????????//?未序列化,先修改屬性
        ????????p.setId(11L);
        ????????oos.writeObject(p);
        ????????//?序列化一次后,再次修改屬性
        ????????p.setId(15L);
        ????????oos.writeObject(p);
        ????????//?序列化兩次后,再次修改屬性
        ????????p.setId(20L);
        ????????oos.writeObject(p);
        ????????oos.close();

        ????????ObjectInputStream?ois?=?new?ObjectInputStream(new?FileInputStream(file));
        ????????Object?people1?=?ois.readObject();
        ????????Object?people2?=?ois.readObject();
        ????????Object?people3?=?ois.readObject();
        ????????ois.close();

        ????????System.out.println(((People)?people1).getId());
        ????????System.out.println(((People)?people2).getId());
        ????????System.out.println(((People)?people3).getId());
        ????}


        ????public?static?void?main(String[]?args)?throws?Exception?{
        ????????sameObjectRepeatedSerialization();
        ????}


        運(yùn)行結(jié)果如下

        ??結(jié)果發(fā)現(xiàn)反序列化讀出的值都是一樣的。說(shuō)明當(dāng)對(duì)象第一次序列化成功后,后續(xù)這個(gè)對(duì)象屬性即使有修改,也不會(huì)對(duì)后面的序列化造成成影響。

        ??這其實(shí)是序列化算法的原因,所有要序列化的對(duì)象都有一個(gè)序列化的編碼號(hào),當(dāng)試圖序列化一個(gè)對(duì)象,會(huì)檢查這個(gè)對(duì)象是否已經(jīng)序列化過(guò),若從未序列化過(guò),才會(huì)序列化為字節(jié)序列去輸出。若已經(jīng)序列化過(guò),則會(huì)輸出一個(gè)編碼符號(hào),不會(huì)重復(fù)序列化一個(gè)對(duì)象。如下

        序列化一次后,后續(xù)繼續(xù)序列化并未重復(fù)轉(zhuǎn)換為字節(jié)序列,而是輸出字符q~

        總結(jié):
        ??當(dāng)?shù)谝淮涡蛄谢螅还苋绾涡薷倪@個(gè)對(duì)象的屬性,都不會(huì)對(duì)后續(xù)的序列化產(chǎn)生影響,反序列化的結(jié)果都和第一次相同。


        ? 作者?|??雙子孤狼

        來(lái)源 |??csdn.net/qq_34115899/article/details/118463573/

        加鋒哥微信:?java1239??
        圍觀鋒哥朋友圈,每天推送Java干貨!

        瀏覽 126
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            深爱丁香 | 挺身驰骋低吼女人呻吟小说网 | 国产免费黄色视频网站 | 美国式禁忌14在线 | 亚洲欧美一区二区三区 | 麻豆成人网址 | 一边喂奶一边挨cao | A级国内。无遮挡影视 | 国模嘉妮超大尺度露私 | 啊操的好爽 |