首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 序列化持久化實戰:MyWorld/CADState 範例與 static 欄位處理

     編輯:Java 相關知識

用序列化保存程式狀態並恢復是誘人想法,但實現前必須解決問題:兩物件共享第三物件參考時如何序列化?恢復後共享參考只出現在一物件上?序列化至獨立檔案再不同部分配載會怎樣?

MyWorld.java:共享參考序列化驗證

//: 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:CAD 系統狀態保存與 static 欄位問題

//: 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;有參數時從檔案恢復。

執行結果揭示 static 問題

>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 雖可序列化但不如預期運作。

修正 CADState.java 三種方案

  1. 為所有形狀添加 serializeStaticState()/deserializeStaticState()
  2. 刪除 shapeTypes Vector 及相關代碼
  3. 在形狀內添加對新序列化/反序列化靜態方法的呼叫

安全性考量

序列化會保存 private 資料。機密欄位應標記 transient,並設計安全保存/恢復機制。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved