transient與序列化
今天檢視hashmap的原始碼,看到一個關鍵詞 transient。
序列化
序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決在對物件流進行讀寫操作時所引發的問題。
序列化的實現:將需要被序列化的類實現Serializable介面,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(物件流)物件,接著,使用ObjectOutputStream物件的writeObject(Object obj)方法就可以將引數為obj的物件寫出(即儲存其狀態),要恢復的話則用輸入流。
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<K,V>[] table = (Entry<K,V>[]) 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<K,V> 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,並自定義完成該欄位中資訊的序列化。