對象序列化就是把對象寫入到輸出資料流中,用來儲存或者傳輸, 對象的還原序列化就是從輸入資料流中讀取對象。
要序列化的對象應該實現Serializable介面,Serializable介面是一個標識介面,沒有抽象方法。
Serializable有一個子介面Externalizable,實現Externalizable介面的類可以自行控制對象序列化荷還原序列化過程。
一般來說,沒有必要自己實現序列化介面,直接交給Java虛擬機器是上策。
實現了序列化介面的類,如果其成員不需要序列化進去,則使用transient關鍵字進行修飾。
下面給出個例子:
import java.io.*;
/**
* Java對象的序列化測試
* File: ObjectStreamTest.java
* User: leizhimin
* Date: 2008-3-12 20:41:43
*/
public class ObjectStreamTest {
public static void main(String args[]) {
testObjectSeri();
testObjectInSeri();
}
/**
* 對象序列化測試
*/
public static void testObjectSeri() {
Person person = new Person("熔岩", "341022225562156", "lavasoft");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream("Q://study//java5study//src//io//person.dat");
oos = new ObjectOutputStream(fos);
oos.writeObject(person);
} catch (FileNotFoundException e) {
System.out.println("找不到指定的檔案!");
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 對象還原序列化測試
*/
public static void testObjectInSeri() {
FileInputStream fis = null;
ObjectInputStream ois = null;
Person person = null;
try {
fis = new FileInputStream("Q://study//java5study//src//io//person.dat");
ois = new ObjectInputStream(fis);
person = (Person) ois.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println(person.toString());
}
}
/**
* 測試序列化所用的類
*/
class Person implements Serializable {
private String username;
private String cardNumber;
private transient String password;
public Person(String username, String cardNumber, String password) {
this.username = username;
this.cardNumber = cardNumber;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String toString() {
StringBuffer sb = new StringBuffer(this.getClass().getName());
sb.append("[");
sb.append("/n/t");
sb.append("username=" + this.username);
sb.append("/n/t");
sb.append("cardNumber=" + this.cardNumber);
sb.append("/n/t");
sb.append("password=" + this.password);
sb.append("]");
return sb.toString();
}
}
運行結果為:
io.Person[
username=熔岩
cardNumber=341022225562156
password=null]
Process finished with exit code 0
屬性password=null,說明在序列化過程中忽略了。
說到此,還有一個容易忽略的問題--serialVersionUID :
序列化運行時使用一個稱為 serialVersionUID 的版本號碼與每個可序列化類別相關聯,該序號在還原序列化過程中用於驗證序列化對象的寄件者和接收者是否為該對象載入了與序列化相容的類。如果接收者載入的該對象的類的 serialVersionUID 與對應的寄件者的類的版本號碼不同,則還原序列化將會導致 InvalidClassException。可序列化類別可以通過聲明名為 "serialVersionUID" 的欄位(該欄位必須是靜態 (static)、最終 (final) 的 long 型欄位)顯式聲明其自己的 serialVersionUID:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
如果可序列化類別未顯式聲明 serialVersionUID,則序列化運行時將基於該類的各個方面計算該類的預設 serialVersionUID 值,如“Java(TM) 對象序列化規範”中所述。不過,強烈建議 所有可序列化類別都顯式聲明 serialVersionUID 值,原因計算預設的 serialVersionUID 對類的詳細資料具有較高的敏感性,根據編譯器實現的不同可能千差萬別,這樣在還原序列化過程中可能會導致意外的 InvalidClassException。因此,為保證 serialVersionUID 值跨不同 java 編譯器實現的一致性,序列化類別必須聲明一個明確的 serialVersionUID 值。還強烈建議使用 private 修改器顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用於立即聲明類 -- serialVersionUID 欄位作為繼承成員沒有用處。
serialVersionUID 在Eclipse裡可以自動產生,可是在其他大部分IDE工具裡面都不能自動產生。但是這個long型值取多少,心裡沒底,與其寫還不如不寫。