- Java核心技術·卷Ⅱ:高級特性(原書第10版)
- (美)凱S.霍斯特曼
- 1911字
- 2020-10-30 18:10:41
2.4.2 理解對象序列化的文件格式
對象序列化是以特殊的文件格式存儲對象數據的,當然,你不必了解文件中表示對象的確切字節序列,就可以使用writeObject/readObject方法。但是,我們發現研究這種數據格式對于洞察對象流化的處理過程非常有益。因為其細節顯得有些專業,所以如果你對其實現不感興趣,則可以跳過這一節。
每個文件都是以下面這兩個字節的“魔幻數字”開始的

后面緊跟著對象序列化格式的版本號,目前是

(我們在本節中統一使用十六進制數字來表示字節。)然后,是它包含的對象序列,其順序即它們存儲的順序。
字符串對象被存為

例如,字符串“Harry”被存為

字符串中的Unicode字符被存儲為修訂過的UTF-8格式。
當存儲一個對象時,這個對象所屬的類也必須存儲。這個類的描述包含
·類名。
·序列化的版本唯一的ID,它是數據域類型和方法簽名的指紋。
·描述序列化方法的標志集。
·對數據域的描述。
指紋是通過對類、超類、接口、域類型和方法簽名按照規范方式排序,然后將安全散列算法(SHA)應用于這些數據而獲得的。
SHA是一種可以為較大的信息塊提供指紋的快速算法,不論最初的數據塊尺寸有多大,這種指紋總是20個字節的數據包。它是通過在數據上執行一個靈巧的位操作序列而創建的,這個序列在本質上可以百分之百地保證無論這些數據以何種方式發生變化,其指紋也都會跟著變化。(關于SHA的更多細節,可以查看一些參考資料,例如William Stallings所著的《Cryptography and Network Security:Principles and Practice》第7版[Prentice Hall,2016]。)但是,序列化機制只使用了SHA碼的前8個字節作為類的指紋。即便這樣,當類的數據域或方法發生變化時,其指紋跟著變化的可能性還是非常大。
在讀入一個對象時,會拿其指紋與它所屬的類的當前指紋進行比對,如果它們不匹配,那么就說明這個類的定義在該對象被寫出之后發生過變化,因此會產生一個異常。在實際情況下,類當然是會演化的,因此對于程序來說,讀入較舊版本的對象可能是必需的。我們將在2.4.5節中討論這個問題。
下面表示了類標識符是如何存儲的:
·72
·2字節的類名長度
·類名
·8字節長的指紋
·1字節長的標志
·2字節長的數據域描述符的數量
·數據域描述符
·78(結束標記)
·超類類型(如果沒有就是70)
標志字節是由在java.io.ObjectStreamConstants中定義的3位掩碼構成的:

我們會在本章稍后討論Externalizable接口。可外部化的類提供了定制的接管其實例域輸出的讀寫方法。我們要寫出的這些類實現了Serializable接口,并且其標志值為02,而可序列化的java.util.Date類定義了它自己的readObject/writeObject方法,并且其標志值為03。
每個數據域描述符的格式如下:
·1字節長的類型編碼
·2字節長的域名長度
·域名
·類名(如果域是對象)
其中類型編碼是下列取值之一:

當類型編碼為L時,域名后面緊跟域的類型。類名和域名字符串不是以字符串編碼74開頭的,但域類型是。域類型使用的是與域名稍有不同的編碼機制,即本地方法使用的格式。
例如,Employee類的薪水域被編碼為:

下面是Employee類完整的類描述符:


這些描述符相當長,如果在文件中再次需要相同的類描述符,可以使用一種縮寫版:

這個序列號將引用到前面已經描述過的類描述符,我們稍后將討論編號模式。
對象將被存儲為:

例如,下面展示的就是Employee對象如何存儲:

正如你所看見的,數據文件包含了足夠的信息來恢復這個Employee對象。
數組總是被存儲成下面的格式:

在類描述符中的數組類名的格式與本地方法中使用的格式相同(它與在其他的類描述符中的類名稍微有些差異)。在這種格式中,類名以L開頭,以分號結束。
例如,3個Employee對象構成的數組寫出時就像下面一樣:

注意,Employee對象數組的指紋與Employee類自身的指紋并不相同。
所有對象(包含數組和字符串)和所有的類描述符在存儲到輸出文件時都被賦予了一個序列號,這個數字以00 7E 00 00開頭。
我們已經看到過,任何給定的類其完整的類描述符只保存一次,后續的描述符將引用它。例如,在前面的示例中,對Date類的重復引用就被編碼為:

相同的機制還被用于對象。如果要寫出一個對之前存儲過的對象的引用,那么這個引用也會以完全相同的方式存儲,即71后面跟隨序列號,從上下文中可以很清楚地了解這個特殊的序列引用表示的是類描述符還是對象。
最后,空引用被存儲為:

下面是前面小節中ObjectRefTest程序的帶注釋的輸出。如果你喜歡,可以運行這個程序,然后查看其數據文件employee.dat的十六進制碼,并將其與注釋列表比較。在輸出中接近結束部分的幾行重要編碼展示了對之前存儲過的對象的引用。


當然,研究這些編碼大概與閱讀常用的電話號碼簿一樣枯燥。了解確切的文件格式確實不那么重要(除非你試圖通過修改數據來達到不可告人的目的),但是對象流對其所包含的所有對象都有詳細描述,并且這些充足的細節可以用來重構對象和對象數組,因此了解它還是大有益處的。
你應該記住:
·對象流輸出中包含所有對象的類型和數據域。
·每個對象都被賦予一個序列號。
·相同對象的重復出現將被存儲為對這個對象的序列號的引用。
- TypeScript Essentials
- Python從小白到大牛
- Data Analysis with IBM SPSS Statistics
- INSTANT CakePHP Starter
- Python自然語言處理(微課版)
- JavaScript動態網頁開發詳解
- Instant Ext.NET Application Development
- Troubleshooting Citrix XenApp?
- RocketMQ實戰與原理解析
- Mastering Concurrency in Python
- 深度學習入門:基于Python的理論與實現
- Python應用與實戰
- Java RESTful Web Service實戰
- 小學生C++趣味編程從入門到精通
- 數據結構與算法詳解