序列化與反序列化

定義

把Java 對象轉換爲字節序列的過程稱爲對象的序列化。把字節序列恢復爲Java 對象的過程稱爲對象的反序列化。

應用場景

(1)實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上(通常存放在文件裏)

(2)利用序列化實現遠程通信,即在網絡上傳送對象的字節序列。

實現方式

所有實現序列化的類都必須實現Serializable 接口,它是一種標記接口,裏面沒有任何方法。當序列化的時候,需要用到ObjectOutputStream 裏面的writeObject();當反序列化的時候,需要用到ObjectInputStream 裏面的readObject()方法。

序列化圖示:


反序列化圖示:


特點

(1)當一個對象被序列化時,只序列化對象的非靜態成員變量,不能序列化任何成員方法和靜態成員變量。

(2)當一個父類實現序列化時,子類自動實現序列化,不需要顯示實現Serializable 接口。

(3)當只有子類實現Serializable接口,父類沒有時,反序列化時生成子類時,只能調用父類的無參構造函數作爲默認的父對象。因此當我們取父對象的變量值時,它的值是調用父類無參構造函數後的值。如果你考慮到這種序列化的情況,在父類無參構造函數中對變量進行初始化,否則的話,父類變量值都是默認聲明的值,如int型的默認是0string型的默認是 null

(4)當一個對象的實例變量引用了其他對象時,序列化該對象時,也把引用對象進行序列化。

(5)對象中被static 或者transient 修飾的變量,在序列化時其變量值是不被保存的。

(6)建議顯示的定義serialVersionUID,默認爲1L,另一種是根據類名、接口名、成員方法及屬性等來生成一個64位的哈希字段 。


顯示定義serialVersionUID的原因

(1)類的不同版本對序列化是否兼容要求

  • 在某些場合,希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID
  • 在某些場合,不希望類的不同版本對序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。 

(2)當你序列化了一個類實例後,希望更改一個字段或添加一個字段,不設置serialVersionUID,所做的任何更改都將導致無法反序化舊有實例,並在反序列化時拋出一個異常java.io.InvalidClassException。如果你添加了serialVersionUID,在反序列舊有實例時,新添加或更改的字段值將設爲初始化值(對象爲null,基本類型爲相應的初始默認值),字段被刪除,將不設置。

(3)提高程序的運行效率。如果在類中沒有顯示聲明serialVersionUID,那麼在序列化的時候會通過計算得到該值。如果顯示聲明該值的話,會省去計算的過程。


序列化算法

序列化算法一般會按步驟做如下事情:

◆ 將對象實例相關的類元數據輸出。

◆ 遞歸地輸出類的超類描述直到不再有超類。

◆ 類元數據完了以後,開始從最頂層的超類開始輸出對象實例的實際數據值。

◆ 從上至下遞歸輸出實例的數據

這一塊可以參考我轉載的一篇博客:http://blog.csdn.net/qq_35181209/article/details/77359985


提問

一個問題:ArrayList 和LinkedList 能否序列化?

都可以序列化。ArrayList 裏面的數組elementData 是聲明爲transient 的,表示ArrayList在序列化的時候,默認不會序列化這些數組元素。

原因:ArrayList 實際上是動態數組,每次在放滿以後會擴容,如果數組擴容後,實際上只放了一個元素,那就會序列化很多null 元素,浪費空間,所以ArrayList 把元素數組設置爲transient,僅僅序列化其他符合要求的實例數據。


對象自定義序列化動作

對象實現Serializable 接口以後,序列化的動作不僅取決於對象本身,還取決於執行序列化的對象。以ObjectOutputStream 爲例,如果ArrayList 或自定義對象實現了writeObject(),readObject(),那麼在序列化和反序列化的時候,就按照自己定義的方法來執行動作,所以ArrayList 就自定義了writeObject 和readObject 方法,然後在writeObject 方法內完成數組元素的自定義序列化動作,在readObject 方法內完成數組元素的自定義反序列化動作。

請看ArrayList自定義序列化反序列化源碼:

    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in capacity
        s.readInt(); // ignored

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章