2007年11月12日 星期一

Head First Java 讀後整理(11) - Saving Objects

1、儲存狀態的選擇,大致有兩個大方向
  • 只有自己寫的 Java 程式會用到這些資料 => 加以序列化(serialization)
  • 資料需要被其他程式引用 => 存於固定格式的資料媒體中(例如:試算表、資料庫 ..... etc)

2、serialize 後的檔案室很難一般人閱讀的,但比純文字更容易讓程式恢復原本的狀態,也比較安全

3、Serialization 的步驟

4、stream 要兩兩連接才能做出有意義的事情 - 其中一個表示連結,另一個則是要被呼叫
以上面 object serialization 為例,由於 FileOutputStream 是很低階的(連結 stream 通常都很低階),可以直接寫入 byte 至檔案中。

但一般的作法不會直接寫入 byte;此時為了保持良好的 OO 設計,需要以物件層次的觀點來寫入,因此需要高階的 stream,即為上面的 ObjectOutputStream,由 ObjectOutputStream 將 object 轉換為 byte,再由 FileOutputStream 將 byte 寫入至檔案。

5、當 object serialization 進行時,除了該 object 的 instance variable 會進行 serialize,所有被參考的 object 也會進行 serialize,而且是全自動化

6、如果要讓 class 能夠被 serialize,就必須實作 Serializable interface(裡面沒有 method 需要實作,僅為標示之用)
import java.io.*;
//沒有 method 需要實作,只是告訴 JVM 它可以被 serialize
public class Box implements Serializable {
//serialize 後,這兩個值會被保留起來
private int width;
private int height;

public void setWidth(int w) {
width = w;
} //close setWidth
public void setHeight(int h) {
height = h;
} //close setHeight

public static void main(String[] args) {
Box myBox = new Box();
myBox.setWidth(50);
myBox.setHeight(20);

try {
FileOutputStream fs = new FileOutputStream("foo.ser");
ObjectOutputStream os = new ObjectOutputStream(fs); //設定連結 stream
os.writeObject(myBox);
os.close();
} catch(Exception ex) {
ex.printStackTrace();
}
} //close main
} //close Box

7、Serialize 是全有或全無的
若物件參考兼有任何一個 class 沒有實作 Serializable interface,就無法進行 serialize,因為 JVM 必須確定 object 狀態能夠完整被保留

8、如果某個 instance variable 不能或不應該 serialize,就將其標示為 transient
transient 的 instance variable,不論儲存當時的值為何,回復之後都會變成 null(object reference) 或是預設值(primitive type)。

但這樣可能會有問題,因此解決方式可以有兩種:
(1) 當 object 被帶回來時,重新初始化 instance variable
(2) 若 transient instance variable 的值很重要,就需要將它的值保存下來,將 object 帶回來的時後才有辦法復原

import java.net.*;
class Chat implements Serializable {
transient String currentID; //此 instance variable 就不會被 serialize 了!
String userName; //這個 instance variable 會被 serialize
//........
//........
}


9、Deserialization 的步驟
  • 建構 FileInputStream
  • 建構 ObjectInputStream
  • 讀取 object (每次讀出一個 object,讀取順序與當初寫入順序是相同的,次數超過會拋出例外)
  • 轉換 object type (回傳的 type 為 Object,因此必須轉換)
  • 關閉 ObjectInputStream

10、object deserialization 的過程
  • object 從 stream 中讀取出來
  • JVM 透過儲存的資訊判斷 object 的 class type
  • JVM 會嘗試尋找並載入 object 所屬的 class,若找不到或無法載入,則會拋出例外
  • 新的 object 會被配置在 heap 上,但 constructor 不會被執行(執行就會初始化了)
  • 若繼承樹中有包含不可 serialize 的 superclass,則從第一個不可 serialize 的 superclass 開始,全部的 constructor 都會執行(r即為初始化)
  • object 的 instance variable 會被還原為 serialize 時的狀態,transient instance variable 會被指派為 null、0、false .... etc

11、正常情況下,不會將 class 也進行 serialize,而會使用 RMI(Remote Method Invocation) 來解決

12、static variable 不會被 serialize,因為所有 object 都是共用同一份 static variable 的值

13、File 物件可以作的事情:(不僅是單純的 file 而已)

14、buffer 的奧妙在於使用了會提升程式運作的效率
藉由 BufferedWriterFileWriter 的連結,BufferedWriter 可以暫存一堆資料,當 buffer 滿的時後再寫入磁碟,這樣可以減少磁碟操作的次數,將強效率

15、使用「serialVersionUID」可避免因為修改 class 而造成 deserialize 時發生無法正確還原的錯誤 (亦即版本控制)
若要查詢 serialVersionUID,可以使用 JDK 提供的「serialver」程式來查詢

沒有留言:

張貼留言