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>

        這些序列化與反序列化的坑你都踩過嗎?

        共 7725字,需瀏覽 16分鐘

         ·

        2022-03-01 18:08

        你知道的越多,不知道的就越多,業(yè)余的像一棵小草!

        你來,我們一起精進(jìn)!你不來,我和你的競爭對手一起精進(jìn)!

        編輯:業(yè)余草

        liuchenyang0515.blog.csdn.net

        推薦:https://www.xttblog.com/?p=5317

        序列化與反序列化的概念

        先說說序列化和反序列化的概念

        ?

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

        ?

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

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

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

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

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

        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?對象


        ?????*/

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

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

        ????????//?用于存儲(chǔ)序列化的文件,這里的java_下劃線僅僅為了說明是java序列化對象,沒有任何其他含義
        ????????File?file?=?new?File("/tmp/people_10.java_");
        ????????if?(!file.exists())?{
        ????????????//?1,先得到文件的上級目錄,并創(chuàng)建上級目錄
        ????????????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)
        ????????);
        ????????//?輸出可序列化對象
        ????????oos.writeObject(p);
        ????????//?關(guān)閉輸出流
        ????????oos.close();

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

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

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

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

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

        多了一個(gè)文件

        控制臺中因反序列化輸出的對象信息打印如下:

        對象信息打印

        子類序列化

        子類實(shí)現(xiàn)Serializable接口,父類沒有實(shí)現(xiàn),子類可以序列化嗎?

        去掉父類Peopleimplements Serializable,讓父類不實(shí)現(xiàn)序列化接口,子類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();
        }

        /**
        ?*?

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


        ?*?*/

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

        ????File?file?=?new?File("/tmp/worker_10.java_");
        ????if?(!file.exists())?{
        ????????//?1,先得到文件的上級目錄,并創(chuàng)建上級目錄
        ????????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)
        ????);
        ????//?輸出可序列化對象
        ????oos.writeObject(p);
        ????//?關(guān)閉輸出流
        ????oos.close();

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

        再次測試運(yùn)行

        再次測試運(yùn)行

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

        我們在父類People中加上空的構(gòu)造方法之后再次執(zhí)行

        空的構(gòu)造方法

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

        ?

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

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

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

        ?

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

        打印結(jié)果

        總結(jié):

        • 子類實(shí)現(xiàn)Serializable接口,父類沒有實(shí)現(xiàn),子類「可以」序列化?。?/section>
        • 這種情況父類一定要提供空構(gòu)造方法,不要忘了子類的toString方法!

        引用對象的序列化

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

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

        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,先得到文件的上級目錄,并創(chuàng)建上級目錄
        ????????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)
        ????);
        ????//?輸出可序列化對象
        ????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é)果如下

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

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

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

        總結(jié):

        ?

        一個(gè)類里面所有的屬性必須是可序列化的,這個(gè)類才能順利的序列化。比如,類中存在引用對象,那么這個(gè)引用對象必須是可序列化的,這個(gè)類才能序列化。

        ?

        同一個(gè)對象多次序列化

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

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

        /**
        ?*?

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


        ?*?*/

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

        ????File?file?=?new?File("/tmp/peopele_more.java_");
        ????if?(!file.exists())?{
        ????????//?1,先得到文件的上級目錄,并創(chuàng)建上級目錄
        ????????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é)果如下

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

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

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

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

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

        總結(jié):

        ?

        當(dāng)?shù)谝淮涡蛄谢?,不管如何修改這個(gè)對象的屬性,都不會(huì)對后續(xù)的序列化產(chǎn)生影響,反序列化的結(jié)果都和第一次相同。

        ?

        歡迎一鍵三連~

        瀏覽 27
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(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>
            黄色网址在线播放 | 亚洲精品suv视频 | 操逼基地 | 国产精品国产三级国产有无不卡 | 97在线视频网站 | 国产精品理论片在线观看 | 午夜影院啪啪 | 高清无码视频在线 | 西西444WWW无码大胆 | 成人午夜免费看 |