transient與序列化
transient的作用:修飾實力域,使其從一個類的預設序列化形式中省略(即預設序列化方式不對該欄位做寫與讀 存取操作) 應用情境:
- 業務需要,不宜做序列化如銀行密碼等 資訊不希望在網路和磁碟等地方儲存,所以可以用transient 聲明,從而保證相應資訊無法從磁碟讀取。(此例源於網上觀點,個人不太贊同)
- 預設的序列化方式不適合,採用自訂序列化的方式例hashMap中對元素的儲存。java 7中hashMap元素的儲存結構為 表 (table)+ 鏈(結點構成的鏈)的儲存結構。其中根據hash(key)求的對應元素在table的索引,並將元素插入此索引處的鏈表。其中的執行個體域table 即為transient
/** * The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry[] table = (Entry[]) EMPTY_TABLE;
那麼問題來了,既然table中儲存著hashMap中的元素資訊,為什麼不序列化? 答:不是不序列化,而是不採用預設的序列化。由於table中元素資訊是我們存取關注的,而非table的結構,所以能合理的完成table中所有資訊的存取是關鍵。鑒於table結構過於複雜(其中的鏈表結構為Entry 結點構成的鏈表),若採用預設的序列化方式,會將table的完整結構(包括各鏈表)鏡像一份,如此以來帶來一下弊端:1. 不僅需要消化大量時間遍曆table結構2. 佔用較多的儲存空間3. 預設序列化對對象圖做遞迴遍曆當表過大時會發生堆疊溢位,所以避免使用預設的序列化方式。 hashMap中採用序列化預存程序中遍曆table中元素,並逐一序列化儲存。而在序列化讀取過程中,根據讀出數值還原表結構的方式來完成,從而提高序列化的品質。過程如下:private void writeObject(java.io.ObjectOutputStream s) throws IOException { // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); …. //自訂完成table中資訊的儲存 // Write out keys and values (alternating) if (size > 0) { for(Map.Entry e : entrySet0()) { s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } private static final long serialVersionUID = 362498820763181265L; /** * Reconstitute the {@code HashMap} instance from a stream (i.e., * deserialize it). */ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold (ignored), loadfactor, and any hidden stuff s.defaultReadObject(); ... //依次讀取,並還原表結構 // Read the keys and values, and put the mappings in the HashMap for (int i = 0; i < mappings; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } }
如此,當自訂中執行個體域儲存機構將複雜,預設序列化方式無法勝任時,可以聲明為transient,並自訂完成該欄位中資訊的序列化。