在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序列化機制採用了一種特殊的序列化演算法:
- 所有儲存到磁碟中的物件都有一個序列化編號。
- 當程式試圖序列化一個物件時,程式先會檢查該物件是否已經被序列化過。只有該物件併為在本次虛擬機器中被序列化過,系統才會將該物件轉換成位元組序列並輸出。
- 如果某個物件已經序列化過,則程式將只是直接輸出一個序列化編號而不是再次重新序列化該物件。
但是要注意的是:如果序列化的是一個可變物件時,只有第一次序列化才會將物件轉換成位元組序列並輸出。當物件的例項變數值已經改變之後,及時再次序列化,也只會輸出前面的序列化編號。
喜歡的話請掃碼支援一下~~