Effective Java - 深入解析序列化byte stream
October 12, 2017這篇文章是閱讀Effective Java 第12章 - Item88之前需要會的知識
關於序列化的Protocol詳細文件在這 不過這篇門檻太高 需要知道什麼是Context Free Grammar 有興趣的各位自動機大神可以來看一下
深談序列化
序列化的目的是為了傳輸或儲存 所以最重要的事情就是序列化的那端用的protocol跟反序列化的那端用的protocol一樣
既然如此 Java就定義了所有人都要共同遵尋的protocol
1.寫下你當下的class的metadata
2.Recursively寫parent class的metadata 寫到java.lang.Object為止
3.再來 寫下class的instance variable的值 從最Parent class的instance開始寫
直上例子
序列化的目標當然是我們最可愛的Person Class
跑以下的程式 就會把Person物件存進person.ser這個文件裡
我們來仔細看看person.ser這個文件
現在看他是天書 五分鐘後這個byte stream在你眼前就會變得非常赤裸
Are you Ready?
庖丁解牛
ACED: MAGIC NUMBER 告訴你說這是段被序列化的byte stream
0005: STREAM_VERSION STREAM的版本
73: 新的Object
72 新的Class descriptor 接下來的是一個新的class
0006 Class name長度 (Person: 6)
506572736F6E ClassName 把16進位的值轉成ASCII
0x50 = 80 = P
0x65 = 101 = e
0x72 = 114 = r
0x73 = 115 = s
0x6F = 111 = o
0x6E = 110 = n
B983994E4F36A9BC serialVersionUID 如果你在你的Person寫死serialVersionUID 那個值就會出現在這裡
02 表示這個object支援序列化
殺進物件內部 繼續描述這個class
0002: 這個物件有多少個instance (name, age 兩個)
49: 第一個variable型態 0x49 = “I” 代表integer
0003: 這個variable長度 3
616765
0x61 = 97 = a
0x67 = 103 = g
0x65 = 101 = e
4C: 這個variable型態 “L” 代表object
0004: 這個variable長度 4
6E616D65
0x6E = 110 = n
0x61 = 97 = a
0x6D = 109 = m
0x65 = 101 = e
74: 接下來是個String (TC_STRING = (byte)0x74;)
0012: 接下來這個String長度是18
4C6A6176612F6C616E672F537472696E673B:
“Ljava/lang/String;”
因為String並不是java的Primitive type 只能算是一個object
那在序列化如何描述一個物件呢 來看一下CFG:
objectDesc:
obj_typecode fieldName className1
原來在byte stream裡面要描述一個物件 需要先說object的type 再說object的name 再說class的name
至於為什麼前面有一個L? JVM會用最簡潔的方式儲存class L[class]; 代表一個class
有興趣可以參考這裡
78 結束對象標誌(TC_ENDBLOCKDATA = (byte)0x78)
這個class描述完了 如果這個class有parent class 那就會從72(點我)開始 recursive繼續描述class
70 Recursive完畢 因為Person沒有父類 所以寫完一個class description就結束(TC_NULL = (byte)0x70;)
看到70就知道 所有class都描述完了 再來 開始記錄object裡面的instance的值
00000001 第一個變數的value
74 接下來是個String (TC_STRING = (byte)0x74;)
0004: 接下來這個String長度是4
4A6F686E:
“John”
總結
因為這個序列化可能會很長 也會有很多class重複用到很多次 java不會每次看到同樣的class還跟第一次看到一樣全部寫上去 所以對於每一個已經寫過的物件或是已經寫過的class descriptor 它會用一個reference serial number記住他
下次再遇到一個一樣的class 我就直接寫那個出現過的class的reference number
下次再遇到一個一樣的object 我就直接寫那個以前寫好的那個object的reference number