對象序列化的目標是將對象保存在磁碟中,或者允許在網路中直接傳輸對象。 對象序列化允許把記憶體中的Java對象轉換成平臺無關的二進位流,從而允許把這種二進位流持久保存在磁碟上或者通過網路將這種二進位流傳輸到另外一個網路節點。 其他程式一旦獲得了這種二進位流,都可以將這種二進位流恢覆成原本的Java對... ...
對象序列化的目標是將對象保存在磁碟中,或者允許在網路中直接傳輸對象。
對象序列化允許把記憶體中的Java對象轉換成平臺無關的二進位流,從而允許把這種二進位流持久保存在磁碟上或者通過網路將這種二進位流傳輸到另外一個網路節點。
其他程式一旦獲得了這種二進位流,都可以將這種二進位流恢覆成原本的Java對象。
序列化的含義和意義
序列化機制允許將實現序列化的Java對象轉換成位元組序列,這些位元組序列可以進行持久化或者通過網路傳輸,使得對象可以脫離程式的運行而獨立存在。
序列化將一個Java對象寫入IO流中。對象的反序列化可以從IO流中恢復該Java對象。
如果想要讓某個對象支持序列化機制,那麼必須讓它的類是可以序列化的。為了讓某個類是可序列化的,那麼該類必須實現如下兩個介面之一:
- Serializable
- Externalizable
使用對象流實現序列化
使用Serializable來實現序列化非常簡單,主要讓目標類實現該標記介面即可,無需實現任何方法。一旦某個類實現了Serializable介面,該類的對象就是可序列化的。
考慮下麵這個例子:
首先展示用來序列化的Person類:
public class Person implements java.io.Serializable{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void talk(){
System.out.println("我是"+name+"我已經"+age+"歲了!");
}
}
接下里的代碼展現瞭如何序列化和反序列化:
import java.io.*;
public class Serializable {
public static void main(String[] args){
try(
//創建一個ObjectOutputStream輸出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:/test.txt"))
)
{
Person per = new Person("AmosH",20);
//將per對象寫入輸出流
oos.writeObject(per);
}catch (IOException ex){
ex.printStackTrace();
}
try(
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:/test.txt"))
)
{
//反序列化per對象,如果知道對象的類型,可以直接採用強制類型轉換成實際類型
Person per = (Person)ois.readObject();
//輸出我是AmosH我已經20歲了!
per.talk();
}catch (Exception ex){
ex.printStackTrace();
}
}
}
需要註意的四點是:
- 反序列化讀取的僅僅是Java對象的數據,而不是Java類,因此採用反序列化回覆Java對象時,必須提供該Java對象所屬類的class文件,否則將會引起ClassNotFoundException異常。
- 當反序列化讀取Java對象時,無需通過構造器來初始化對象。
- 如果使用序列化機制在文件中寫入了多個Java對象,使用反序列化機制恢復對象時必須按實際寫入的順序讀取。
- 當一個可序列化類有多個父類(包括直接父類和間接父類),這些父類要麼包含無參數的構造器,要麼也是可序列化的。否則反序列化時將會拋出InvalidClassException異常。如果父類是不可序列化的,只是帶有無參數的構造器,則該父類中定義的成員變數值不會序列化到二進位流中。
對象引用的序列化
當被序列化的對象中包含的成員變數的類型不是基本類型而是一個引用類型時,那麼這個引用類必須是可序列化的,否則擁有該類型成員變數的類也是不可序列化的。
我們假設如下一種情況:
程式中有一個Student對象和兩個Teacher對象,兩個Teacher對象中都有成員變數引用這個Student對象。
在這樣的情形下,如果我們序列化這三個對象,因為程式在序列化Teacher對象時也會將其中的引用對象實例序列化,那麼似乎程式會向輸出流中輸出三個Person對象。
如果程式想輸出流中輸出了三個Person對象,那麼程式從輸入流中反序列化這些對象時,將會得到三個Student對象,兩個Teacher對象所引用的Student對象將不是同一個對象,這顯然也違背了Java序列化機制的初衷。
所以,Java序列化機制採用了一種特殊的序列化演算法:
- 所有保存到磁碟中的對象都有一個序列化編號。
- 當程式試圖序列化一個對象時,程式先會檢查該對象是否已經被序列化過。只有該對象併為在本次虛擬機中被序列化過,系統才會將該對象轉換成位元組序列並輸出。
- 如果某個對象已經序列化過,則程式將只是直接輸出一個序列化編號而不是再次重新序列化該對象。
但是要註意的是:如果序列化的是一個可變對象時,只有第一次序列化才會將對象轉換成位元組序列並輸出。當對象的實例變數值已經改變之後,及時再次序列化,也只會輸出前面的序列化編號。