用序列化保存程式狀態並恢復是誘人想法,但實現前必須解決問題:兩物件共享第三物件參考時如何序列化?恢復後共享參考只出現在一物件上?序列化至獨立檔案再不同部分配載會怎樣?
//: MyWorld.java
import java.io.*; import java.util.*;
class House implements Serializable {}
class Animal implements Serializable {
String name; House preferredHouse;
Animal(String nm, House h) { name = nm; preferredHouse = h; }
public String toString() { return name + "[" + super.toString() + "], " + preferredHouse + "\n"; }
}
public class MyWorld {
public static void main(String[] args) {
House house = new House();
Vector animals = new Vector();
animals.addElement(new Animal("Bosco the dog", house));
animals.addElement(new Animal("Ralph the hamster", house));
animals.addElement(new Animal("Fronk the cat", house));
System.out.println("animals: " + animals);
try {
ByteArrayOutputStream buf1 = new ByteArrayOutputStream();
ObjectOutputStream o1 = new ObjectOutputStream(buf1);
o1.writeObject(animals);
o1.writeObject(animals); // 寫入第二組
ByteArrayOutputStream buf2 = new ByteArrayOutputStream();
ObjectOutputStream o2 = new ObjectOutputStream(buf2);
o2.writeObject(animals); // 不同流
// 讀回
ObjectInputStream in1 = new ObjectInputStream(new ByteArrayInputStream(buf1.toByteArray()));
ObjectInputStream in2 = new ObjectInputStream(new ByteArrayInputStream(buf2.toByteArray()));
Vector animals1 = (Vector)in1.readObject();
Vector animals2 = (Vector)in1.readObject();
Vector animals3 = (Vector)in2.readObject();
System.out.println("animals1: " + animals1);
System.out.println("animals2: " + animals2);
System.out.println("animals3: " + animals3);
} catch(Exception e) { e.printStackTrace(); }
}
} ///:~
有趣之處:可對位元組陣列序列化,實現任意 Serializable 物件「全面複製」(整個物件網而非基本物件與參考)。複製問題第 12 章詳細講述。
Animal 含 House 欄位。main() 建立 Animal Vector 序列化兩次送入兩流。重新裝配輸出:
animals: [Bosco the dog[Animal@1cc76c], House@1cc769 , ... 共享 House@1cc769] animals1: [Bosco the dog[Animal@1cca0c], House@1cca16 , ... 共享 House@1cca16] animals2: [Bosco the dog[Animal@1cca0c], House@1cca16 , ... 同 animals1] animals3: [Bosco the dog[Animal@1cca52], House@1cca5c , ... 完全不同物件網]
animals1/animals2 同流讀回,共享 House 參考位址相同。animals3 從不同流讀回,系統不知是化身,產生全新物件網。
結論:所有東西序列化到單一資料流可恢復與寫入時完全相同的物件網。寫入首尾物件間可改變狀態,但序列化時物件採當時狀態(含連接關係)寫入。
當作「微觀」操作序列化:將構成系統狀態的所有物件置入單一集合,一次操作完成寫入。同樣一次呼叫即可成功恢復。
//: CADState.java
abstract class Shape implements Serializable {
public static final int RED = 1, BLUE = 2, GREEN = 3;
private int xPos, yPos, dimension;
private static Random r = new Random();
private static int counter = 0;
abstract public void setColor(int newColor);
abstract public int getColor();
public Shape(int xVal, int yVal, int dim) { xPos = xVal; yPos = yVal; dimension = dim; }
public String toString() { return getClass().toString() + " color[" + getColor() + "] xPos[" + xPos + "] yPos[" + yPos + "] dim[" + dimension + "]\n"; }
public static Shape randomFactory() { /* 隨機建立 Circle/Square/Line */ }
}
class Circle extends Shape { private static int color = RED; /* ... */ }
class Square extends Shape { private static int color; /* ... */ }
class Line extends Shape {
private static int color = RED;
public static void serializeStaticState(ObjectOutputStream os) throws IOException { os.writeInt(color); }
public static void deserializeStaticState(ObjectInputStream os) throws IOException { color = os.readInt(); }
/* ... */
}
public class CADState {
public static void main(String[] args) throws Exception {
Vector shapeTypes, shapes;
if(args.length == 0) {
shapeTypes = new Vector(); shapes = new Vector();
shapeTypes.addElement(Circle.class); shapeTypes.addElement(Square.class); shapeTypes.addElement(Line.class);
for(int i = 0; i < 10; i++) shapes.addElement(Shape.randomFactory());
for(int i = 0; i < 10; i++) ((Shape)shapes.elementAt(i)).setColor(Shape.GREEN);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("CADState.out"));
out.writeObject(shapeTypes);
Line.serializeStaticState(out); // 手動序列化 static
out.writeObject(shapes);
} else {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(args[0]));
shapeTypes = (Vector)in.readObject();
Line.deserializeStaticState(in); // 手動反序列化 static
shapes = (Vector)in.readObject();
}
System.out.println(shapes);
}
} ///:~
Shape 實作 Serializable,衍生類自動可序列化。每衍生類含 static color 決定該型別所有實例顏色。main() 無參數時建立狀態並序列化至 CADState.out;有參數時從檔案恢復。
>java CADState [class Circle color[3] ... 皆為 GREEN(3)] >java CADState CADState.out [class Circle color[1] ... RED(1), Square color[0] ... 預設 0, Line color[3] ... GREEN(3)]
xPos/yPos/dim 成功保存恢復,但 static color 失敗。Circle 變 1(RED 定義值),Square 變 0(建構子初始化值),Line 因手動序列化保持 3。Class 雖可序列化但不如預期運作。
serializeStaticState()/deserializeStaticState()shapeTypes Vector 及相關代碼序列化會保存 private 資料。機密欄位應標記 transient,並設計安全保存/恢復機制。