java序列化機制和原理

 Java序列化算法透析 html

  Serialization(序列化)是一種將對象以一連串的字節描述的過程;反序列化deserialization是一種將這些字節重建成一個對象的過程。Java序列化API提供一種處理對象序列化的標準機制。在這裏你能學到如何序列化一個對象,何時須要序列化以及Java序列化的算法,咱們用一個實例來示範序列化之後的字節是如何描述一個對象的信息的。 java

序列化的必要性 web

Java中,一切都是對象,在分佈式環境中常常須要將Object從這一端網絡或設備傳遞到另外一端。
這就須要有一種能夠在兩端傳輸數據的協議。
Java序列化機制就是爲了解決這個問題而產生。 算法

如何序列化一個對象 網絡

一個對象可以序列化的前提是實現Serializable接口,Serializable接口沒有方法,更像是個標記。
有了這個標記的
Class就能被序列化機制處理。 分佈式

import java.io.Serializable; class TestSerial implements Serializable {          
    public byte version = 100; public byte count = 0; }

而後咱們寫個程序將對象序列化並輸出。ObjectOutputStream能把Object輸出成Byte流。
咱們將
Byte流暫時存儲到temp.out文件裏。  spa

public static void main(String args[]) throws IOException {          
    FileOutputStream fos = new FileOutputStream("temp.out");         
    ObjectOutputStream oos = new ObjectOutputStream(fos);          
    TestSerial ts = new TestSerial();          
    oos.writeObject(ts);          
    oos.flush();          
    oos.close(); 
}

    若是要從持久的文件中讀取Bytes重建對象,咱們可使用ObjectInputStream
    public static void main(String args[]) throws IOException {     
        FileInputStream fis = new FileInputStream("temp.out");          
        ObjectInputStream oin = new ObjectInputStream(fis);           
        TestSerial ts = (TestSerial) oin.readObject();           
        System.out.println("version="+ts.version);    
    }

    執行結果爲 100.
    對象的序列化格式
    將一個對象序列化後是什麼樣子呢?打開剛纔咱們將對象序列化輸出的temp.out文件
    16進制方式顯示。內容應該以下:
    AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65 73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05 63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78 70 00 64 
    這一坨字節就是用來描述序列化之後的TestSerial對象的,咱們注意到TestSerial類中只有兩個域:
    public byte version = 100;
    public byte count = 0;
    且都是byte型,理論上存儲這兩個域只須要2byte,可是實際上temp.out佔據空間爲51bytes,也就是說除了數據之外,還包括了對序列化對象的其餘描述
    Java的序列化算法
    序列化算法通常會按步驟作以下事情:
    將對象實例相關的類元數據輸出。
    遞歸地輸出類的超類描述直到再也不有超類。
    類元數據完了之後,開始從最頂層的超類開始輸出對象實例的實際數據值。
    從上至下遞歸輸出實例的數據
    咱們用另外一個更完整覆蓋全部可能出現的狀況的例子來講明:
      class parent implements Serializable {          
          int parentVersion = 10; }

    class contain implements Serializable{          
        Int containVersion = 11; }

    public class SerialTest extends parent implements Serializable {   
           int version = 66; contain con = new contain();   
           public int getVersion() { return version; }  
           public static void main(String args[]) throws IOException {  
                  FileOutputStream fos = new FileOutputStream("temp.out");  
                  ObjectOutputStream oos = new ObjectOutputStream(fos);  
                  SerialTest st = new SerialTest();  
                  oos.writeObject(st);  
                  oos.flush();  
                  oos.close();  
           }  
    }
  •  
    1. AC ED: STREAM_MAGIC.  聲明使用了序列化協議 .
    2. 00 05: STREAM_VERSION.  序列化協議版本 .
    3. 0x73: TC_OBJECT.   聲明這是一個新的對象 .   
    1. 0x72: TC_CLASSDESC.  聲明這裏開始一個新 Class
    2. 00 0A: Class 名字的長度 .
    3. 53 65 72 69 61 6c 54 65 73 74: SerialTest,Class 類名 .
    4. 05 52 81 5A AC 66 02 F6: SerialVersionUID,  序列化 ID ,若是沒有指定,
      則會由算法隨機生成一個
      8byte ID.
    5. 0x02:  標記號 .  該值聲明該對象支持序列化。
    6. 00 02:  該類所包含的域個數。
    1. 0x49:  域類型 . 49  表明 "I",  也就是 Int.
    2. 00 07:  域名字的長度 .
    3. 76 65 72 73 69 6F 6E: version, 域名字描述 .
    1. 0x4C:  域的類型 .
    2. 00 03:  域名字長度 .
    3. 63 6F 6E:  域名字描述, con
    4. 0x74: TC_STRING.  表明一個 new String. String 來引用對象。
    5. 00 09:  String 長度 .
    6. 4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, JVM 的標準對象簽名表示法 .
    7. 0x78: TC_ENDBLOCKDATA, 對象數據塊結束的標誌
    1. 0x72: TC_CLASSDESC.  聲明這個是個新類 .
    2. 00 06:  類名長度 .
    3. 70 61 72 65 6E 74: parent, 類名描述。
    4. 0E DB D2 BD 85 EE 63 7A: SerialVersionUID,  序列化 ID.
    5. 0x02:  標記號 .  該值聲明該對象支持序列化 .
    6. 00 01:  類中域的個數 .
    1. 0x49:  域類型 . 49  表明 "I",  也就是 Int.
    2. 00 0D:  域名字長度 .
    3. 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion ,域名字描述。
    4. 0x78: TC_ENDBLOCKDATA, 對象塊結束的標誌。
    5. 0x70: TC_NULL,  說明沒有其餘超類的標誌。 .
    1. 00 00  00  0A: 10, parentVersion 域的值 .
    1. 00 00  00  42: 66, version 域的值 .
    1. 0x73: TC_OBJECT,  聲明這是一個新的對象 .
    2. 0x72: TC_CLASSDESC 聲明這裏開始一個新 Class.
    3. 00 07:  類名的長度 .
    4. 63 6F 6E 74 61 69 6E: contain, 類名描述 .
    5. FC BB E6 0E FB CB 60 C7: SerialVersionUID,  序列化 ID.
    6. 0x02: Various flags.  標記號 .  該值聲明該對象支持序列化
    7. 00 01:  類內的域個數。
    1. 0x49:  域類型 . 49  表明 "I",  也就是 Int..
    2. 00 0E:  域名字長度 .
    3. 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion,  域名字描述 .
    4. 0x78: TC_ENDBLOCKDATA 對象塊結束的標誌 .
    1. 0x70:TC_NULL ,沒有超類了。
    1. 00 00  00  0B: 11, containVersion 的值 .

    這個例子是至關的直白啦。SerialTest類實現Parent超類,內部還持有一個Container對象。 code

    序列化後的格式以下: htm

    AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65 對象

    73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07

    76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09

    4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72

    65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00

    0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70

    00 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74

    61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00

    0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78

    70 00 00 00 0B

    咱們來仔細看看這些字節都表明了啥。開頭部分,見顏色

    序列化算法的第一步就是輸出對象相關類的描述。例子所示對象爲SerialTest類實例,
    所以接下來輸出
    SerialTest類的描述。見顏色

    接下來,算法輸出其中的一個域,int version=66;見顏色

    而後,算法輸出下一個域,contain con = new contain();這個有點特殊,是個對象。
    描述對象類型引用時須要使用
    JVM的標準對象簽名表示法,見顏色

    .接下來算法就會輸出超類也就是Parent類描述了,見顏色

    下一步,輸出parent類的域描述,int parentVersion=100;同見顏色

    到此爲止,算法已經對全部的類的描述都作了輸出。下一步就是把實例對象的實際值輸出了。這時候是從parent Class的域開始的,見顏色

    還有SerialTest類的域:

    再日後的bytes比較有意思,算法須要描述contain類的信息,要記住,
    如今尚未對
    contain類進行過描述,見顏色

    .輸出contain的惟一的域描述,int containVersion=11

    這時,序列化算法會檢查contain是否有超類,若是有的話會接着輸出。

    最後,將contain類實際域值輸出。

    OK,咱們討論了java序列化的機制和原理,但願能對同窗們有所幫助。

serialVersionUID值的重要做用
          根據上面的分析,能夠發現若是一個類可序列化,serialVersionUID建議給一個肯定的值,不要由系統自動生成,不然在增減字段(不能修改字段類型及長度)時,若是兩邊的類的版本不一樣會致使反序列化失敗.
注意問題
若是序列化時代碼這樣寫:
SerialTest st = new SerialTest(); 
oos.writeObject((parent)st);

會發現序列化的對象依然是SerialTest,若是在分佈式環境中用Parent反序列化(調用段不存在SerialTest),會形成ClassNotFoundException.
redcreen
相關文章
相關標籤/搜索