1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問(wèn)題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      Java中如何實(shí)現(xiàn)序列化與反序列化

      這篇文章給大家介紹Java中如何實(shí)現(xiàn)序列化與反序列化,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

      專注于為中小企業(yè)提供網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站建設(shè)服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)曲麻萊免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了近千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

      序列化與反序列化

      Java對(duì)象是有生命周期的,當(dāng)生命周期結(jié)束它就會(huì)被回收,但是可以通過(guò)將其轉(zhuǎn)換為字節(jié)序列永久保存下來(lái)或者通過(guò)網(wǎng)絡(luò)傳輸給另一方。

      把對(duì)象轉(zhuǎn)換為字節(jié)序列的過(guò)程稱為對(duì)象的序列化;把字節(jié)序列恢復(fù)為對(duì)象的過(guò)程稱為對(duì)象的反序列化。

      Serializable接口

      一個(gè)類實(shí)現(xiàn)java.io.Serializable接口就可以被序列化或者反序列化。實(shí)際上,Serializable接口中沒(méi)有任何變量和方法,它只是一個(gè)標(biāo)識(shí)。如果沒(méi)有實(shí)現(xiàn)這個(gè)接口,在序列化或者反序列化時(shí)會(huì)拋出NotSerializableException異常。

      下面是一個(gè)實(shí)現(xiàn)了Serializable接口的類以及它的序列化與反序列化過(guò)程。

      public class SerialTest {
        public static void main(String[] args) {
          Test test = new Test();
          test.setName("test");
          // 序列化,存儲(chǔ)對(duì)象到文本
          ObjectOutputStream oos = null;
          try {
            oos = new ObjectOutputStream(new FileOutputStream("test"));
            oos.writeObject(test);
          } catch (IOException e) {
            e.printStackTrace();
          } finally {
            try {
              if (oos != null) {
                oos.close();
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
          // 反序列化,從文本中取出對(duì)象
          ObjectInputStream ois = null;
          try {
            ois = new ObjectInputStream(new FileInputStream("test"));
            Test1 test1 = (Test1) ois.readObject();
          } catch (IOException e) {
            e.printStackTrace();
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          } finally {
            try {
              if (ois != null) {
                ois.close();
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      }
      class Test implements Serializable {
        private String name;
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        @Override
        public String toString() {
          return "Test{" +
              "name='" + name + '\'' +
              '}';
        }
      }

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

      Test{name='test'}

      serialVersionUID

      private static final long serialVersionUID = -3297006450515623366L;

      serialVersionUID是一個(gè)序列化版本號(hào),實(shí)現(xiàn)Serializable接口的類都會(huì)有一個(gè)版本號(hào)。如果沒(méi)有自己定義,那么程序會(huì)默認(rèn)生成一個(gè)版本號(hào),這個(gè)版本號(hào)是Java運(yùn)行時(shí)環(huán)境根據(jù)類的內(nèi)部細(xì)節(jié)自動(dòng)生成的。最好我們自己定義該版本號(hào),否則當(dāng)類發(fā)生改變時(shí),程序?yàn)槲覀冏詣?dòng)生成的序列化版本號(hào)也會(huì)發(fā)生改變,那么再將原來(lái)的字節(jié)序列反序列化時(shí)就會(huì)發(fā)生錯(cuò)誤。

      下面是將Test1類加入一個(gè)變量age,此時(shí)再進(jìn)行反序列化的結(jié)果。可以看出,序列化版本號(hào)已發(fā)生改變,程序認(rèn)為不是同一個(gè)類,不能進(jìn)行反序列化。

      java.io.InvalidClassException: test.Test1; local class incompatible: stream classdesc serialVersionUID = 9097989105451761251, local class serialVersionUID = -7756223913249050270
       at java.base/java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:689)
       at java.base/java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1903)
       at java.base/java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1772)
       at java.base/java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2060)
       at java.base/java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1594)
       at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:430)
       at test.SerialTest.main(SerialTest.java:11)

      為了提高serialVersionUID的獨(dú)立性和確定性,強(qiáng)烈建議在一個(gè)可序列化類中顯示地定義serialVersionUID,為他賦予明確的值。

      那么在IDEA中,怎么手動(dòng)生成呢?

      在settings->Editor->Inspections下,搜索serial,開(kāi)啟Serializable class without 'serialVersionUID'的拼寫(xiě)檢查,然后將光標(biāo)放在實(shí)現(xiàn)Serializable的接口上,按住ALt+Enter鍵,選擇添加serialVersionUID即可。

      Transient關(guān)鍵字

      transient修飾類的變量,可以使變量不被序列化。反序列化時(shí),被transient修飾的變量的值被設(shè)為初始值,如int類型被設(shè)為0,對(duì)象型被設(shè)為null。

      ObjectOutputStream類和ObjectInputStream類

      ObjectOutputStream的writeObject方法可以序列化對(duì)象,ObjectInputStream的readObject可以反序列化對(duì)象。ObjectOutputStream實(shí)現(xiàn)了接口ObjectOutput,所以可以進(jìn)行對(duì)象寫(xiě)操作。ObjectInputStream實(shí)現(xiàn)了接口ObjectInput,所以可以對(duì)對(duì)象進(jìn)行讀操作。

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

      給Test類中增加一個(gè)靜態(tài)變量,賦值為12,然后在序列化之后修改其值為10,反序列化之后打印它的值。發(fā)現(xiàn)打印的值為10,之前的12并沒(méi)有被保存。

      靜態(tài)變量是不參與序列化的,序列化只是用來(lái)保存對(duì)象的狀態(tài),而靜態(tài)變量屬于類的狀態(tài)。

      父類序列化

      讓Test繼承一個(gè)沒(méi)有實(shí)現(xiàn)Serializable接口的類,設(shè)置父類中變量的值,對(duì)Test類的實(shí)例進(jìn)行序列化與反序列化操作。

      public class SerialTest {
        public static void main(String[] args) {
          Test test = new Test();
          test.setName("huihui");
          test.setSex(12);
          // 序列化,存儲(chǔ)對(duì)象到文本
          ObjectOutputStream oos = null;
          try {
            oos = new ObjectOutputStream(new FileOutputStream("test"));
            oos.writeObject(test);
          } catch (IOException e) {
            e.printStackTrace();
          } finally {
            try {
              if (oos != null) {
                oos.close();
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
          // 反序列化,從文本中取出對(duì)象
          ObjectInputStream ois = null;
          try {
            ois = new ObjectInputStream(new FileInputStream("test"));
            Test test1 = (Test) ois.readObject();
            System.out.println(test1);
          } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
          } finally {
            try {
              if (ois != null) {
                ois.close();
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      }
      class Test extends TestFather implements Serializable {
        private static final long serialVersionUID = 4335715933640891747L;
        private String name;
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        @Override
        public String toString() {
          return "Test{" +
              "name='" + name + '\'' +
              "sex='" + sex + '\'' +
              '}';
        }
      }
      class TestFather {
        protected Integer sex;
        public Integer getSex() {
          return sex;
        }
        public void setSex(Integer sex) {
          this.sex = sex;
        }
        @Override
        public String toString() {
          return "TestFather{" +
              "sex='" + sex + '\'' +
              '}';
        }
      }

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

      Test{name='huihui'sex='null'}

      發(fā)現(xiàn)雖然對(duì)sex進(jìn)行了復(fù)制,但是反序列化結(jié)果仍然為null。

      現(xiàn)在讓TestFather類實(shí)現(xiàn)Serializable接口,運(yùn)行結(jié)果如下。所以當(dāng)我們想要序列化父類的變量時(shí),也需要讓父類實(shí)現(xiàn)Serializable接口。

      Test{name='huihui'sex='12'}

      同理,如果Test類中有任何變量是對(duì)象,那么該對(duì)象的類也需要實(shí)現(xiàn)Serializable接口。查看String源代碼,確實(shí)實(shí)現(xiàn)了Serializable接口。大家可以測(cè)試一下字段的類不實(shí)現(xiàn)Serializable接口的情況,運(yùn)行會(huì)直接報(bào)java.io.NotSerializableException異常。

      敏感字段加密

      如果對(duì)于某些字段我們并不想直接暴露出去,需要對(duì)其進(jìn)行加密處理,那么就需要我們自定義序列化和反序列化方法。使用Serializable接口進(jìn)行序列化時(shí),如果不自定義方法,則默認(rèn)調(diào)用ObjectOutputStream的defaultWriteObject方法和ObjectInputStream的defaultReadObject方法。下面我們來(lái)嘗試一下自己實(shí)現(xiàn)序列化與反序列化過(guò)程。

      class Test implements Serializable {
        private static final long serialVersionUID = 4335715933640891747L;
        private String name;
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        @Override
        public String toString() {
          return "Test{" +
              "name='" + name + '\'' +
              '}';
        }
        private void writeObject(ObjectOutputStream out) {
          try {
            ObjectOutputStream.PutField putField = out.putFields();
            System.out.println("原name:" + name);
            // 模擬加密
            name = "change";
            putField.put("name", name);
            System.out.println("加密后的name:" + name);
            out.writeFields();
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
        private void readObject(ObjectInputStream in) {
          try {
            ObjectInputStream.GetField getField = in.readFields();
            Object object = getField.get("name", "");
            System.out.println("要解密的name:" + object.toString());
            name = "huihui";
          } catch (IOException e) {
            e.printStackTrace();
          } catch (ClassNotFoundException e) {
            e.printStackTrace();
          }
        }
      }

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

      原name:huihui
      加密后的name:change
      要解密的name:change
      解密后的name:huihui

      這種寫(xiě)法重寫(xiě)了writeObject方法和readObject方法,下面一種接口也可以實(shí)現(xiàn)相同的功能。

      Externalizable接口

      除了Serializable接口,Java還提供了一個(gè)Externalizable接口,它繼承了Serializable接口,提供了writeExternal和readExternal兩個(gè)方法,實(shí)現(xiàn)該接口的類必須重寫(xiě)這兩個(gè)方法。同時(shí)還發(fā)現(xiàn),類還必須提供一個(gè)無(wú)參構(gòu)造方法,否則會(huì)報(bào)java.io.InvalidClassException異常。

      先不深究為什么要加一個(gè)無(wú)參構(gòu)造方法,我們先試一下這個(gè)接口的序列化效果。將類Test改為如下所示:

      class Test implements Externalizable {
        private static final long serialVersionUID = 4335715933640891747L;
        private String name;
        public Test() {
        }
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        @Override
        public String toString() {
          return "Test{" +
              "name='" + name + '\'' +
              '}';
        }
        @Override
        public void writeExternal(ObjectOutput out) throws IOException {
        }
        @Override
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        }
      }

      再次運(yùn)行測(cè)試方法,發(fā)現(xiàn)輸出的name是null。在readObject處打斷點(diǎn),發(fā)現(xiàn)會(huì)調(diào)用無(wú)參構(gòu)造方法。

      name其實(shí)并沒(méi)有被序列化與反序列化,writeExternal方法和readExternal方法中是需要我們自己來(lái)實(shí)現(xiàn)序列化與反序列化的細(xì)節(jié)的。在反序列化時(shí),會(huì)首先調(diào)用類的無(wú)參考構(gòu)造方法創(chuàng)建一個(gè)新對(duì)象,然后再填充每個(gè)字段。

      我們對(duì)writeExternal方法和readExternal方法進(jìn)行重寫(xiě):

      @Override
      public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
      }
      @Override
      public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
      }

      此時(shí)運(yùn)行測(cè)試方法,發(fā)現(xiàn)Test類被正常序列化與反序列化。

      序列化存儲(chǔ)規(guī)則

      當(dāng)多次序列化一個(gè)對(duì)象時(shí),是會(huì)序列化多次還是會(huì)序列化一次呢?

      public class SerialTest {
        public static void main(String[] args) {
          Test test = new Test();
          test.setName("huihui");
          // 序列化,存儲(chǔ)對(duì)象到文本
          ObjectOutputStream oos = null;
          try {
            oos = new ObjectOutputStream(new FileOutputStream("test"));
            // 兩次寫(xiě)入文件
            oos.writeObject(test);
            oos.flush();
            System.out.println(new File("test").length());
            oos.writeObject(test);
            oos.flush();
            System.out.println(new File("test").length());
          } catch (IOException e) {
            e.printStackTrace();
          } finally {
            try {
              if (oos != null) {
                oos.close();
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
          // 反序列化,從文本中取出對(duì)象
          ObjectInputStream ois = null;
          try {
            ois = new ObjectInputStream(new FileInputStream("test"));
            // 讀取兩個(gè)對(duì)象
            Test test1 = (Test) ois.readObject();
            Test test2 = (Test) ois.readObject();
            System.out.println(test1 == test1);
          } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
          } finally {
            try {
              if (ois != null) {
                ois.close();
              }
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      }
      class Test implements Serializable {
        private static final long serialVersionUID = 4335715933640891747L;
        private String name;
        public String getName() {
          return name;
        }
        public void setName(String name) {
          this.name = name;
        }
        @Override
        public String toString() {
          return "Test{" +
              "name='" + name + '\'' +
              '}';
        }
      }

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

      73
      78
      true

      關(guān)于Java中如何實(shí)現(xiàn)序列化與反序列化就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。


      分享文章:Java中如何實(shí)現(xiàn)序列化與反序列化
      文章起源:http://www.ef60e0e.cn/article/geidie.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        砚山县| 鄂尔多斯市| 麟游县| 乐昌市| 新和县| 花莲县| 房产| 石柱| 明水县| 宝应县| 上高县| 深水埗区| 冀州市| 阿尔山市| 泰州市| 定陶县| 福海县| 高安市| 且末县| 北辰区| 乌兰察布市| 达日县| 绩溪县| 临夏市| 盱眙县| 江川县| 阳谷县| 巧家县| 静乐县| 科技| 钟山县| 阆中市| 包头市| 华坪县| 乐清市| 阿克陶县| 吉隆县| 克什克腾旗| 南漳县| 道孚县| 广德县|