Java 之 Serializable 序列化和還原序列化的概念,作用的通俗易懂的解釋

來源:互聯網
上載者:User

標籤:com   手動   blog   tin   版本號碼   編輯器   需要   java se   數字   

遇到這個 Java Serializable 序列化這個介面,我們可能會有如下的問題
a,什麼叫序列化和還原序列化
b,作用。為啥要實現這個 Serializable 介面,也就是為啥要序列化
c,serialVersionUID 這個的值到底是在怎麼設定的,有什麼用。有的是1L,有的是一長串數字,迷惑ing。
我剛剛見到這個關鍵字 Serializable 的時候,就有如上的這麼些問題。

在處理這個問題之前,你要Crowdsourced Security Testing道一個問題,這個比較重要。
這個Serializable介面,以及相關的東西,全部都在 Java io 裡面的。

1,序列化和還原序列化的概念

序列化:把對象轉換為位元組序列的過程稱為對象的序列化。
還原序列化:把位元組序列恢複為對象的過程稱為對象的還原序列化。

上面是專業的解釋,現在來點通俗的解釋。在代碼啟動並執行時候,我們可以看到很多的對象(debug過的都造吧),
可以是一個,也可以是一類對象的集合,很多的對象資料,這些資料中,
有些資訊我們想讓他持久的儲存起來,那麼這個序列化。
就是把記憶體裡面的這些對象給變成一連串的位元組描述的過程。
常見的就是變成檔案
我不序列化也可以儲存檔案啥的呀,有什麼影響呢?我也是這麼問的。

2,什麼情況下需要序列化 

當你想把的記憶體中的對象狀態儲存到一個檔案中或者資料庫中時候;
當你想用通訊端在網路上傳送對象的時候;
當你想通過RMI傳輸對象的時候;

(老實說,上面的幾種,我可能就用過個存資料庫的)

 

3,如何?序列化

實現Serializable介面即可

 

上面這些理論都比較簡單,下面實際代碼看看這個序列化到底能幹啥,以及會產生的bug問題。

先上對象代碼,飛豬.java

 

  1.  package com.lxk.model;
  2.   
  3.  import java.io.Serializable;
  4.   
  5.  /**
  6.   * @author lxk on 2017/11/1
  7.   */
  8.  public class FlyPig implements Serializable {
  9.   //private static final long serialVersionUID = 1L;
  10.   private static String AGE = "269";
  11.   private String name;
  12.   private String color;
  13.   transient private String car;
  14.   
  15.   //private String addTip;
  16.   
  17.   public String getName() {
  18.   return name;
  19.   }
  20.   
  21.   public void setName(String name) {
  22.   this.name = name;
  23.   }
  24.   
  25.   public String getColor() {
  26.   return color;
  27.   }
  28.   
  29.   public void setColor(String color) {
  30.   this.color = color;
  31.   }
  32.   
  33.   public String getCar() {
  34.   return car;
  35.   }
  36.   
  37.   public void setCar(String car) {
  38.   this.car = car;
  39.   }
  40.   
  41.   //public String getAddTip() {
  42.   // return addTip;
  43.   //}
  44.   //
  45.   //public void setAddTip(String addTip) {
  46.   // this.addTip = addTip;
  47.   //}
  48.   
  49.   @Override
  50.   public String toString() {
  51.   return "FlyPig{" +
  52.   "name=‘" + name + ‘\‘‘ +
  53.   ", color=‘" + color + ‘\‘‘ +
  54.   ", car=‘" + car + ‘\‘‘ +
  55.   ", AGE=‘" + AGE + ‘\‘‘ +
  56.   //", addTip=‘" + addTip + ‘\‘‘ +
  57.   ‘}‘;
  58.   }
  59.  }


注意下,注釋的代碼,是一會兒要各種情況下使用的。

 

下面就是main方法啦

 

  1.  package com.lxk.test;
  2.   
  3.  import com.lxk.model.FlyPig;
  4.   
  5.  import java.io.*;
  6.   
  7.  /**
  8.   * 序列化測試
  9.   *
  10.   * @author lxk on 2017/11/1
  11.   */
  12.  public class SerializableTest {
  13.   public static void main(String[] args) throws Exception {
  14.   serializeFlyPig();
  15.   FlyPig flyPig = deserializeFlyPig();
  16.   System.out.println(flyPig.toString());
  17.   
  18.   }
  19.   
  20.   /**
  21.   * 序列化
  22.   */
  23.   private static void serializeFlyPig() throws IOException {
  24.   FlyPig flyPig = new FlyPig();
  25.   flyPig.setColor("black");
  26.   flyPig.setName("naruto");
  27.   flyPig.setCar("0000");
  28.   // ObjectOutputStream 對象輸出資料流,將 flyPig Object Storage Service到E盤的 flyPig.txt 檔案中,完成對 flyPig 對象的序列化操作
  29.   ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/flyPig.txt")));
  30.   oos.writeObject(flyPig);
  31.   System.out.println("FlyPig 對象序列化成功!");
  32.   oos.close();
  33.   }
  34.   
  35.   /**
  36.   * 還原序列化
  37.   */
  38.   private static FlyPig deserializeFlyPig() throws Exception {
  39.   ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/flyPig.txt")));
  40.   FlyPig person = (FlyPig) ois.readObject();
  41.   System.out.println("FlyPig 對象還原序列化成功!");
  42.   return person;
  43.   }
  44.  }

 

 

對上面的2個操作檔案流的類的簡單說明

ObjectOutputStream代表對象輸出資料流:

它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的位元組序列寫到一個目標輸出資料流中。

ObjectInputStream代表對象輸入資料流:

它的readObject()方法從一個源輸入資料流中讀取位元組序列,再把它們還原序列化為一個對象,並將其返回。

 

具體怎麼看運行情況。

第一種:上來就這些代碼,不動,直接run,看效果。

實際運行結果,他會在 d:/flyPig.txt 產生個檔案。

從運行結果上看:

1,他實現了對象的序列化和還原序列化。

2,transient 修飾的屬性,是不會被序列化的。我設定的奧迪四個圈的車不見啦,成了null。my god。

3,你先別著急說,這個靜態變數AGE也被序列化啦。這個得另測。

 

第二種:為了驗證這個靜態屬效能不能被序列化和還原序列化,可如下操作。

 

  1.   public static void main(String[] args) throws Exception {
  2.   serializeFlyPig();
  3.   //FlyPig flyPig = deserializeFlyPig();
  4.   //System.out.println(flyPig.toString());
  5.   }

 

這個完了之後,意思也就是說,你先序列化個對象到檔案了。這個對象是帶靜態變數的static。

現在修改flyPig類裡面的AGE的值,給改成26吧。

然後,看裡面的運行代碼和執行結果。

可以看到,剛剛序列化的269,沒有讀出來。而是剛剛修改的26,如果可以的話,應該是覆蓋這個26,是269才對。

所以,得出結論,這個靜態static的屬性,他不序列化。

 

第三種:示範這個 serialVersionUID 的作用和用法

最暴力的改法,直接把model的類實現的這個介面去掉。然後執行後面的序列化和還原序列化的方法。直接報錯。

拋異常:NotSerializableException

這個太暴力啦,不推薦這麼幹。

然後就是,還和上面的操作差不多,先是單獨執行序列化方法。組建檔案。
然後,開啟屬性 addTip ,這之後,再次執行還原序列化方法,看現象。

拋異常:InvalidClassException  詳情如下。

InvalidClassException: com.lxk.model.FlyPig; 
local class incompatible: 
stream classdesc serialVersionUID = -3983502914954951240, 
local class serialVersionUID = 7565838717623951575

解釋一下:

因為我再model裡面是沒有明確的給這個 serialVersionUID 賦值,但是,Java會自動的給我賦值的,

這個值跟這個model的屬性相關計算出來的。

我儲存的時候,也就是我序列化的時候,那時候還沒有這個addTip屬性呢,

所以,自動產生的serialVersionUID 這個值,

在我還原序列化的時候Java自動產生的這個serialVersionUID值是不同的,他就拋異常啦。

(你還可以反過來,帶ID去序列化,然後,沒ID去還原序列化。也是同樣的問題。)

 

再來一次,就是先序列化,這個時候,把 private static final long serialVersionUID = 1L; 這行代碼的注釋開啟。那個addTip屬性先注釋掉

序列化之後,再把這個屬性開啟,再還原序列化。看看什麼情況。

這個時候,代碼執行OK,一切正常。good。

 

這個現象對我們有什麼意義:

老鐵,這個意義比較大,首先,你要是不知道這個序列化是幹啥的,萬一他真的如開頭所講的那樣存資料庫啦,socket傳輸啦,rmi傳輸啦。雖然我也不知道這是幹啥的。你就給model bean 實現了個這個介面,你沒寫這個 serialVersionUID 那麼在後來擴充的時候,可能就會出現不認識舊資料的bug,那不就炸啦嗎。回憶一下上面的這個出錯情況。想想都可怕,這個鍋誰來背? 

所以,有這麼個理論,就是在實現這個Serializable 介面的時候,一定要給這個 serialVersionUID 賦值,就是這麼個問題。

這也就解釋了,我們剛剛開始編碼的時候,實現了這個介面之後,為啥eclipse編輯器要黃色警告,需要添加個這個ID的值。而且還是一長串你都不知道怎麼來的數字。

 

下面解釋這個 serialVersionUID 的值到底怎麼設定才OK。

首先,你可以不用自己去賦值,Java會給你賦值,但是,這個就會出現上面的bug,很不安全,所以,還得自己手動的來。

那麼,我該怎麼賦值,eclipse可能會自動給你賦值個一長串數字。這個是沒必要的。

可以簡單的賦值個 1L,這就可以啦。。這樣可以確保代碼一致時還原序列化成功。

不同的serialVersionUID的值,會影響到還原序列化,也就是資料的讀取,你寫1L,注意L大些。電腦是不區分大小寫,但是,作為觀眾的我們,是要區分1和L的l,所以說,這個值,閑的沒事不要亂動,不然一個版本升級,舊資料就不相容了,你還不知道問題在哪。。。

 

下面是摘自 jdk api 文檔裡面關於介面 Serializable 的描述
類通過實現 java.io.Serializable 介面以啟用其序列化功能。
未實現此介面的類將無法使其任何狀態序列化或還原序列化。
可序列化類別的所有子類型本身都是可序列化的。因為實現介面也是間接的等同於繼承。
序列化介面沒有方法或欄位,僅用於標識可序列化的語義。

關於 serialVersionUID 的描述
序列化運行時使用一個稱為 serialVersionUID 的版本號碼與每個可序列化類別相關聯,該序號在還原序列化過程中用於驗證序列化對象的寄件者和接收者是否為該對象載入了與序列化相容的類。如果接收者載入的該對象的類的 serialVersionUID 與對應的寄件者的類的版本號碼不同,則還原序列化將會導致 InvalidClassException。可序列化類別可以通過聲明名為 "serialVersionUID" 的欄位(該欄位必須是靜態 (static)、最終 (final) 的 long 型欄位)顯式聲明其自己的 serialVersionUID:

如果可序列化類別未顯式聲明 serialVersionUID,則序列化運行時將基於該類的各個方面計算該類的預設 serialVersionUID 值,如“Java(TM) 對象序列化規範”中所述。不過,強烈建議 所有可序列化類別都顯式聲明 serialVersionUID 值,原因是計算預設的 serialVersionUID 對類的詳細資料具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在還原序列化過程中可能會導致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類別必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修飾符顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用於直接聲明類 -- serialVersionUID 欄位作為繼承成員沒有用處。數組類不能聲明一個明確的 serialVersionUID,因此它們總是具有預設的計算值,但是數組類沒有匹配 serialVersionUID 值的要求

 

78544505

 

Java 之 Serializable 序列化和還原序列化的概念,作用的通俗易懂的解釋

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.