有關Java對象的序列化和反序列化也算是Java基礎的一部分,下面對Java序列化的機制和原理進行一些介紹。 Java序列化算法透析 Serialization(序列化)是一種將對象以一連串的字節描述的過程;反序列化deserialization是一種將這些字節重建成一個對象的過程。Java序列化API提供一種處理對象序列化的標準機制。在這裏你能學到如何序列化一個對象,何時須要序列化以及Java序列化的算法,咱們用一個實例來示範序列化之後的字節是如何描述一個對象的信息的。 序列化的必要性 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文件裏。 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型,理論上存儲這兩個域只須要2個byte,可是實際上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(); } } 這個例子是至關的直白啦。SerialTest類實現了Parent超類,內部還持有一個Container對象。 序列化後的格式以下: 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 咱們來仔細看看這些字節都表明了啥。開頭部分,見顏色: AC ED: STREAM_MAGIC. 聲明使用了序列化協議. 00 05: STREAM_VERSION. 序列化協議版本. 0x73: TC_OBJECT. 聲明這是一個新的對象. 序列化算法的第一步就是輸出對象相關類的描述。例子所示對象爲SerialTest類實例,所以接下來輸出SerialTest類的描述。見顏色: 0x72: TC_CLASSDESC. 聲明這裏開始一個新Class。 00 0A: Class名字的長度. 53 65 72 69 61 6c 54 65 73 74: SerialTest,Class類名. 05 52 81 5A AC 66 02 F6: SerialVersionUID, 序列化ID,若是沒有指定,則會由算法隨機生成一個8byte的ID. 0x02: 標記號. 該值聲明該對象支持序列化。 00 02: 該類所包含的域個數。 接下來,算法輸出其中的一個域,int version=66;見顏色: 0x49: 域類型. 49 表明"I", 也就是Int. 00 07: 域名字的長度. 76 65 72 73 69 6F 6E: version,域名字描述. 而後,算法輸出下一個域,contain con = new contain();這個有點特殊,是個對象。描述對象類型引用時須要使用JVM的標準對象簽名表示法,見顏色: 0x4C: 域的類型. 00 03: 域名字長度. 63 6F 6E: 域名字描述,con 0x74: TC_STRING. 表明一個new String.用String來引用對象。 00 09: 該String長度. 4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, JVM的標準對象簽名表示法. 0x78: TC_ENDBLOCKDATA,對象數據塊結束的標誌 .接下來算法就會輸出超類也就是Parent類描述了,見顏色: 0x72: TC_CLASSDESC. 聲明這個是個新類. 00 06: 類名長度. 70 61 72 65 6E 74: parent,類名描述。 0E DB D2 BD 85 EE 63 7A: SerialVersionUID, 序列化ID. 0x02: 標記號. 該值聲明該對象支持序列化. 00 01: 類中域的個數. 下一步,輸出parent類的域描述,int parentVersion=100;同見顏色: 0x49: 域類型. 49 表明"I", 也就是Int. 00 0D: 域名字長度. 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion,域名字描述。 0x78: TC_ENDBLOCKDATA,對象塊結束的標誌。 0x70: TC_NULL, 說明沒有其餘超類的標誌。. 到此爲止,算法已經對全部的類的描述都作了輸出。下一步就是把實例對象的實際值輸出了。這時候是從parent Class的域開始的,見顏色: 00 00 00 0A: 10, parentVersion域的值. 還有SerialTest類的域: 00 00 00 42: 66, version域的值. 再日後的bytes比較有意思,算法須要描述contain類的信息,要記住,如今尚未對contain類進行過描述,見顏色: 0x73: TC_OBJECT, 聲明這是一個新的對象. 0x72: TC_CLASSDESC聲明這裏開始一個新Class. 00 07: 類名的長度. 63 6F 6E 74 61 69 6E: contain,類名描述. FC BB E6 0E FB CB 60 C7: SerialVersionUID, 序列化ID. 0x02: Various flags. 標記號. 該值聲明該對象支持序列化 00 01: 類內的域個數。 .輸出contain的惟一的域描述,int containVersion=11; 0x49: 域類型. 49 表明"I", 也就是Int.. 00 0E: 域名字長度. 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, 域名字描述. 0x78: TC_ENDBLOCKDATA對象塊結束的標誌. 這時,序列化算法會檢查contain是否有超類,若是有的話會接着輸出。 0x70:TC_NULL,沒有超類了。 最後,將contain類實際域值輸出。 00 00 00 0B: 11, containVersion的值.java