1. 程式人生 > >Serializable 序列化和反序列化

Serializable 序列化和反序列化

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

我剛剛見到這個關鍵字 Serializable 的時候,就有如上的這麼些問題。

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

1,序列化和反序列化的概念

序列化:把物件轉換為位元組序列的過程稱為物件的序列化。

反序列化:把位元組序列恢復為物件的過程稱為物件的反序列化。

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

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

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

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

3,如何實現序列化

實現Serializable介面即可

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

先上物件程式碼,飛豬.java

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

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

下面就是main方法啦

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

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

ObjectOutputStream代表物件輸出流:

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

ObjectInputStream代表物件輸入流:

它的readObject()方法從一個源輸入流中讀取位元組序列,再把它們反序列化為一個物件,並將其返回。

具體怎麼看執行情況。

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

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

從執行結果上看:

1,他實現了物件的序列化和反序列化。

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

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

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

  1. publicstaticvoid 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傳輸啦。雖然我也不