1 將序列化對象寫入文件
一般流程:
//可能會產生異常
try {
//打開文件
FileOutputStream fileStream=new FileOutputStream("MyGame.ser");
//創建對象輸出流
ObjectOutputStream oStream=new ObjectOutputStream(fileStream);
//寫入對象
oStream.writeObject(new Integer(1));
//關閉對象輸出流
oStream.close();
//關閉文件流
fileStream.close();
}catch (Exception e) {
e.printStackTrace();// : handle exception
}
- 對象存儲了什麼?
在存儲對象時,我們想的是恢復對象後能和存儲時有一樣的狀態。但是如果在對象中有其它對象成員呢,我們知道在對象中保存的實際是其它對象的引用,真正的對象在堆上其它地方。如果只保存此對象的引用,顯然在恢復時會出錯誤。
序列化程序會將對象版圖上的所有東西存儲起來。被對象的實例變量所引用的所有對象都會被序列化。
例如:zoo對象中有Lion[] lionArray對象,lionArray對象中又有多個lion,那麼在存儲zoo時,lionArray和其中的lion都會被序列化存儲。
- 可序列化接口Serializable與不可序列化關鍵詞transient
如果要讓自己設計的類可以被序列化存儲,就實現Serializable接口,不需要實現任何方法。實現了此接口的類,其子類也可以序列化。
如果要讓一個類可序列化,那麼此類中的所有成員都應該是可序列化的,即整個對象版圖都必須正確地序列化,不然就全部失敗。
如果某實例變量不能或不應該被序列化,就把它標記爲transient(瞬時)的。
某類可序列化但其父類不可序列化的情況:
當對象被還原且它的父類不可序列化時,父類的構造函數會跟創建新的對象一樣執行。
有多個引用指向同一個對象的情況:
只有一個對象會被存儲,恢復後原引用仍指向同一對象。
如果某個實例被標記爲transient的:
那麼在恢復時會默認設爲null,如果該值很重要,可通過其它方法保存其值,待恢復後再將值賦給它。
2 解序列化:還原對象
一般流程:
try {
//打開文件
FileInputStream fileStream=new FileInputStream("MyGame.ser");
//創建對象輸入流
ObjectInputStream iStream=new ObjectInputStream(fileStream);
//讀取對象
Object oneObject=iStream.readObject();
//轉換對象
Integer aInteger=(Integer)oneObject;
//關閉對象輸入流
iStream.close();
//關閉文件輸入流
fileStream.close();
} catch (Exception e) {
e.printStackTrace();// TODO: handle exception
}
- 對象恢復時發生了什麼
在恢復對象時,java虛擬機首先通過存儲信息判斷對象的class類型,如果無法加載該類型則拋出異常。
新的對象被配置在堆上,但其構造函數不會執行,如果其某個父類是不可序列化的,則該父類及其之上的父類的構造函數會執行。
未被標記爲transient的變量會恢復成存儲時的內容,被標記爲transient的變量會被設置爲0、false或null。
靜態變量不會被序列化。
3 版本控制
如果對象被保存後,它原來的類被修改了,這樣在讀取對象時就有可能會出錯。例如原來類中的某個變量被刪除了,或者類型被修改等等。有些修改會損害解序列化,有些不會。
會損害解序列化的修改 | 通常不會有事的修改 |
---|---|
刪除實例變量 | 加入新的實例變量(會使用默認值) |
改變實例變量的類型 | 在繼承層次中加入新的類 |
將非瞬時的實例變量改爲瞬時的 | 從繼承層次中刪除類 |
改變類的繼承層次 | 不會影響解序列化程序設定變量值的存取層次修改 |
將類從可序列化改成不可序列化的 | 將實例變量從瞬時改成非瞬時(會使用默認值) |
將實例變量改成靜態的 |
每當對象被序列化的同時,該對象都會被加上一個類的版本識別ID,被稱爲serialVersionUID,在還原對象時,會對比對象與java虛擬機上類的serialVersionUID,只有相同纔會判定爲相同的類。
在修改了類之後通常修改了類之後其serialVersionUID會發生改變,也就是還原對象時會找不到其類型。有一個解決方法是將serialVersionUID放在類中,使其固定。
使用serialver工具可以查詢某個類的serialVersionUID,再將其賦給類中的static final long serialVersionUID變量
public class Dog{
static final long serialVersionUID=-3480159834908509L;
//其它代碼
}