<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          又一個難題!Java 序列化和反序列化為什么要實現(xiàn) Serializable 接口

          共 7455字,需瀏覽 15分鐘

           ·

          2020-10-03 15:07

          程序員的成長之路
          互聯(lián)網(wǎng)/程序員/技術(shù)/資料共享?
          關(guān)注


          閱讀本文大概需要 6 分鐘。

          來自:blog.csdn.net/litianxiang_kaola

          最近公司的在做服務(wù)化, 需要把所有 model 包里的類都實現(xiàn) Serializable 接口, 同時還要顯示指定 serialVersionUID 的值. 聽到這個需求, 我腦海里就突然出現(xiàn)了好幾個問題, 比如說:
          • 序列化和反序列化是什么?
          • 實現(xiàn)序列化和反序列化為什么要實現(xiàn) Serializable 接口?
          • 實現(xiàn) Serializable 接口就算了, 為什么還要顯示指定 serialVersionUID 的值?
          • 我要為 serialVersionUID 指定個什么值?
          下面我們來一一解答這幾個問題.

          序列化和反序列化

          • 序列化:把對象轉(zhuǎn)換為字節(jié)序列的過程稱為對象的序列化.
          • 反序列化:把字節(jié)序列恢復(fù)為對象的過程稱為對象的反序列化.

          什么時候需要用到序列化和反序列化呢?

          當我們只在本地 JVM 里運行下 Java 實例, 這個時候是不需要什么序列化和反序列化的, 但當我們需要將內(nèi)存中的對象持久化到磁盤, 數(shù)據(jù)庫中時, 當我們需要與瀏覽器進行交互時, 當我們需要實現(xiàn) RPC 時, 這個時候就需要序列化和反序列化了.
          前兩個需要用到序列化和反序列化的場景, 是不是讓我們有一個很大的疑問? 我們在與瀏覽器交互時, 還有將內(nèi)存中的對象持久化到數(shù)據(jù)庫中時, 好像都沒有去進行序列化和反序列化, 因為我們都沒有實現(xiàn) Serializable 接口, 但一直正常運行.
          下面先給出結(jié)論:
          只要我們對內(nèi)存中的對象進行持久化或網(wǎng)絡(luò)傳輸, 這個時候都需要序列化和反序列化.
          理由:
          服務(wù)器與瀏覽器交互時真的沒有用到 Serializable 接口嗎? JSON 格式實際上就是將一個對象轉(zhuǎn)化為字符串, 所以服務(wù)器與瀏覽器交互時的數(shù)據(jù)格式其實是字符串, 我們來看來 String 類型的源碼:

          public?final?class?String
          ????implements?java.io.Serializable,?Comparable<String>,?CharSequence?
          {
          ????/\*\*?The?value?is?used?for?character?storage.?\*/
          ????private?final?char?value\[\];

          ????/\*\*?Cache?the?hash?code?for?the?string?\*/
          ????private?int?hash;?//?Default?to?0

          ????/\*\*?use?serialVersionUID?from?JDK?1.0.2?for?interoperability?\*/
          ????private?static?final?long?serialVersionUID?=?-6849794470754667710L;

          ????......
          }

          String 類型實現(xiàn)了 Serializable 接口, 并顯示指定 serialVersionUID 的值.
          然后我們再來看對象持久化到數(shù)據(jù)庫中時的情況, Mybatis 數(shù)據(jù)庫映射文件里的 insert 代碼:

          <insert?id="insertUser"?parameterType="org.tyshawn.bean.User">
          ????INSERT?INTO?t\_user(name,?age)?VALUES?(#{name},?#{age})
          insert>

          實際上我們并不是將整個對象持久化到數(shù)據(jù)庫中, 而是將對象中的屬性持久化到數(shù)據(jù)庫中, 而這些屬性都是實現(xiàn)了 Serializable 接口的基本屬性.

          實現(xiàn)序列化和反序列化為什么要實現(xiàn) Serializable 接口?

          在 Java 中實現(xiàn)了 Serializable 接口后, JVM 會在底層幫我們實現(xiàn)序列化和反序列化, 如果我們不實現(xiàn) Serializable 接口, 那自己去寫一套序列化和反序列化代碼也行, 至于具體怎么寫, Google 一下你就知道了.

          實現(xiàn) Serializable 接口就算了, 為什么還要顯示指定 serialVersionUID 的值?

          如果不顯示指定 serialVersionUID, JVM 在序列化時會根據(jù)屬性自動生成一個 serialVersionUID, 然后與屬性一起序列化, 再進行持久化或網(wǎng)絡(luò)傳輸. 在反序列化時, JVM 會再根據(jù)屬性自動生成一個新版 serialVersionUID, 然后將這個新版 serialVersionUID 與序列化時生成的舊版 serialVersionUID 進行比較, 如果相同則反序列化成功, 否則報錯.
          如果顯示指定了 serialVersionUID, JVM 在序列化和反序列化時仍然都會生成一個 serialVersionUID, 但值為我們顯示指定的值, 這樣在反序列化時新舊版本的 serialVersionUID 就一致了.
          在實際開發(fā)中, 不顯示指定 serialVersionUID 的情況會導(dǎo)致什么問題? 如果我們的類寫完后不再修改, 那當然不會有問題, 但這在實際開發(fā)中是不可能的, 我們的類會不斷迭代, 一旦類被修改了, 那舊對象反序列化就會報錯. 所以在實際開發(fā)中, 我們都會顯示指定一個 serialVersionUID, 值是多少無所謂, 只要不變就行.
          寫個實例測試下:
          (1) User 類
          不顯示指定 serialVersionUID.

          public?class?User?implements?Serializable?{

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

          ????public?String?getName()?{
          ????????return?name;
          ????}

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

          ????public?Integer?getAge()?{
          ????????return?age;
          ????}

          ????public?void?setAge(Integer?age)?{
          ????????this.age?=?age;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"User{"?+
          ????????????????"?+?name?+?'\\''?+
          ????????????????"
          ,?age="?+?age?+
          ????????????????'}';
          ????}
          }

          (2) 測試類
          先進行序列化, 再進行反序列化.

          public?class?SerializableTest?{

          ????private?static?void?serialize(User?user)?throws?Exception?{
          ????????ObjectOutputStream?oos?=?new?ObjectOutputStream(new?FileOutputStream(new?File("D:\\\\111.txt")));
          ????????oos.writeObject(user);
          ????????oos.close();
          ????}

          ????private?static?User?deserialize()?throws?Exception{
          ????????ObjectInputStream?ois?=?new?ObjectInputStream(new?FileInputStream(new?File("D:\\\\111.txt")));
          ????????return?(User)?ois.readObject();
          ????}


          ????public?static?void?main(String\[\]?args)?throws?Exception?{
          ????????User?user?=?new?User();
          ????????user.setName("tyshawn");
          ????????user.setAge(18);
          ????????System.out.println("序列化前的結(jié)果:?"?+?user);

          ????????serialize(user);

          ????????User?dUser?=?deserialize();
          ????????System.out.println("反序列化后的結(jié)果:?"+?dUser);
          ????}
          }

          (3) 結(jié)果
          先注釋掉反序列化代碼, 執(zhí)行序列化代碼, 然后 User 類新增一個屬性 sex

          public?class?User?implements?Serializable?{

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

          ????public?String?getName()?{
          ????????return?name;
          ????}

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

          ????public?Integer?getAge()?{
          ????????return?age;
          ????}

          ????public?void?setAge(Integer?age)?{
          ????????this.age?=?age;
          ????}

          ????public?String?getSex()?{
          ????????return?sex;
          ????}

          ????public?void?setSex(String?sex)?{
          ????????this.sex?=?sex;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"User{"?+
          ????????????????"?+?name?+?'\\''?+
          ????????????????"
          ,?age="?+?age?+
          ????????????????"
          ,?sex='"?+?sex?+?'\\''?+
          ????????????????'}';
          ????}
          }

          再注釋掉序列化代碼執(zhí)行反序列化代碼, 最后結(jié)果如下:
          序列化前的結(jié)果: User{name='tyshawn', age=18} Exception in thread "main" java.io.InvalidClassException: org.tyshawn.SerializeAndDeserialize.User; local class incompatible: stream classdesc serialVersionUID = 1035612825366363028, local class serialVersionUID = -1830850955895931978
          報錯結(jié)果為序列化與反序列化產(chǎn)生的 serialVersionUID 不一致.
          接下來我們在上面 User 類的基礎(chǔ)上顯示指定一個 serialVersionUID

          private?static?final?long?serialVersionUID?=?1L;

          再執(zhí)行上述步驟, 測試結(jié)果如下:
          序列化前的結(jié)果: User{name='tyshawn', age=18} 反序列化后的結(jié)果: User{name='tyshawn', age=18, sex='null'}
          顯示指定 serialVersionUID 后就解決了序列化與反序列化產(chǎn)生的 serialVersionUID 不一致的問題.

          Java 序列化的其他特性

          先說結(jié)論, 被 transient 關(guān)鍵字修飾的屬性不會被序列化, static 屬性也不會被序列化.
          我們來測試下這個結(jié)論:
          (1) User 類

          public?class?User?implements?Serializable?{
          ????private?static?final?long?serialVersionUID?=?1L;

          ????private?String?name;
          ????private?Integer?age;
          ????private?transient?String?sex;
          ????private?static?String?signature?=?"你眼中的世界就是你自己的樣子";

          ????public?String?getName()?{
          ????????return?name;
          ????}

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

          ????public?Integer?getAge()?{
          ????????return?age;
          ????}

          ????public?void?setAge(Integer?age)?{
          ????????this.age?=?age;
          ????}

          ????public?String?getSex()?{
          ????????return?sex;
          ????}

          ????public?void?setSex(String?sex)?{
          ????????this.sex?=?sex;
          ????}

          ????public?static?String?getSignature()?{
          ????????return?signature;
          ????}

          ????public?static?void?setSignature(String?signature)?{
          ????????User.signature?=?signature;
          ????}

          ????@Override
          ????public?String?toString()?{
          ????????return?"User{"?+
          ????????????????"?+?name?+?'\\''?+
          ????????????????"
          ,?age="?+?age?+
          ????????????????"
          ,?sex='"?+?sex?+'\\''?+
          ????????????????",?signature='"?+?signature?+?'\\''?+
          ????????????????'
          }';
          ????}
          }

          (2) 測試類

          public?class?SerializableTest?{

          ????private?static?void?serialize(User?user)?throws?Exception?{
          ????????ObjectOutputStream?oos?=?new?ObjectOutputStream(new?FileOutputStream(new?File("D:\\\\111.txt")));
          ????????oos.writeObject(user);
          ????????oos.close();
          ????}

          ????private?static?User?deserialize()?throws?Exception{
          ????????ObjectInputStream?ois?=?new?ObjectInputStream(new?FileInputStream(new?File("D:\\\\111.txt")));
          ????????return?(User)?ois.readObject();
          ????}


          ????public?static?void?main(String\[\]?args)?throws?Exception?{
          ????????User?user?=?new?User();
          ????????user.setName("tyshawn");
          ????????user.setAge(18);
          ????????user.setSex("man");
          ????????System.out.println("序列化前的結(jié)果:?"?+?user);

          ????????serialize(user);

          ????????User?dUser?=?deserialize();
          ????????System.out.println("反序列化后的結(jié)果:?"+?dUser);
          ????}
          }

          (3) 結(jié)果
          先注釋掉反序列化代碼, 執(zhí)行序列化代碼, 然后修改 User 類 signature = “我的眼里只有你”, 再注釋掉序列化代碼執(zhí)行反序列化代碼, 最后結(jié)果如下:
          序列化前的結(jié)果: User{name='tyshawn', age=18, sex='man', signature='你眼中的世界就是你自己的樣子'} 反序列化后的結(jié)果: User{name='tyshawn', age=18, sex='null', signature='我的眼里只有你'}

          static 屬性為什么不會被序列化?

          因為序列化是針對對象而言的, 而 static 屬性優(yōu)先于對象存在, 隨著類的加載而加載, 所以不會被序列化.
          看到這個結(jié)論, 是不是有人會問, serialVersionUID 也被 static 修飾, 為什么 serialVersionUID 會被序列化? 其實 serialVersionUID 屬性并沒有被序列化, JVM 在序列化對象時會自動生成一個 serialVersionUID, 然后將我們顯示指定的 serialVersionUID 屬性值賦給自動生成的 serialVersionUID.

          推薦閱讀:

          哈哈哈,程序員之間的鄙視鏈,總結(jié)全了!

          項目中常用的19條MySQL優(yōu)化

          5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復(fù)「2048」,即可免費獲?。。?/span>

          微信掃描二維碼,關(guān)注我的公眾號

          朕已閱?

          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  最新三级片在线播放 | 日本尻屄视频 | 蜜乳一区二区三区有限公司 | 欧美人与禽性XXXXX杂性 欧美日韩一区二区三区,麻豆 | 麻豆精品秘 国产视频 |