定義
把Java 對象轉換爲字節序列的過程稱爲對象的序列化。把字節序列恢復爲Java 對象的過程稱爲對象的反序列化。
應用場景
(1)實現了數據的持久化,通過序列化可以把數據永久地保存到硬盤上(通常存放在文件裏)
(2)利用序列化實現遠程通信,即在網絡上傳送對象的字節序列。
實現方式
所有實現序列化的類都必須實現Serializable 接口,它是一種標記接口,裏面沒有任何方法。當序列化的時候,需要用到ObjectOutputStream 裏面的writeObject();當反序列化的時候,需要用到ObjectInputStream 裏面的readObject()方法。
序列化圖示:
反序列化圖示:
特點
(1)當一個對象被序列化時,只序列化對象的非靜態成員變量,不能序列化任何成員方法和靜態成員變量。
(2)當一個父類實現序列化時,子類自動實現序列化,不需要顯示實現Serializable 接口。
(3)當只有子類實現Serializable接口,父類沒有時,反序列化時生成子類時,只能調用父類的無參構造函數作爲默認的父對象。因此當我們取父對象的變量值時,它的值是調用父類無參構造函數後的值。如果你考慮到這種序列化的情況,在父類無參構造函數中對變量進行初始化,否則的話,父類變量值都是默認聲明的值,如int型的默認是0,string型的默認是
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();
}
}
}