1. 程式人生 > >transient與序列化

transient與序列化

今天檢視hashmap的原始碼,看到一個關鍵詞 transient。

序列化
序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決在對物件流進行讀寫操作時所引發的問題。
序列化的實現:將需要被序列化的類實現Serializable介面,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(物件流)物件,接著,使用ObjectOutputStream物件的writeObject(Object obj)方法就可以將引數為obj的物件寫出(即儲存其狀態),要恢復的話則用輸入流。

transient
修飾實力域,使其從一個類的預設序列化形式中省略(即預設序列化方式不對該欄位做寫與讀 存取操作)

應用場景

  1. 業務需要,不宜做序列化

如銀行密碼等 資訊不希望在網路和磁碟等地方儲存,所以可以用 transient 宣告,從而保證相應資訊無法從磁碟讀取。

  1. 預設的序列化方式不適合,採用自定義序列化的方式

例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,並自定義完成該欄位中資訊的序列化。