java中的序列化(serialization)機制能夠?qū)⒁粋€實例對象的狀態(tài)信息寫入到一個字節(jié)流中,,使其可以通過socket進行傳輸,、或者持久化存儲到數(shù)據(jù)庫或文件系統(tǒng)中;然后在需要的時候,,可以根據(jù)字節(jié)流中的信息來重構(gòu)一個相同的對象,。序列化機制在java中有著廣泛的應(yīng)用,EJB、RMI等技術(shù)都是以此為基礎(chǔ)的,。
正確使用序列化機制 一般而言,,要使得一個類可以序列化,只需簡單實現(xiàn)java.io.Serializable接口即可,。該接口是一個標記式接口,,它本身不包含任何內(nèi)容,實現(xiàn)了該接口則表示這個類準備支持序列化的功能,。如下例定義了類Person,,并聲明其可以序列化。
public class Person implements java.io.Serializable {} 序列化機制是通過java.io.ObjectOutputStream類和java.io.ObjectInputStream類來實現(xiàn)的,。在序列化(serialize)一個對象的時候,,會先實例化一個ObjectOutputStream對象,然后調(diào)用其writeObject()方法,;在反序列化(deserialize)的時候,,則會實例化一個ObjectInputStream對象,然后調(diào)用其readObject()方法,。下例說明了這一過程,。
public void serializeObject(){ String fileName = "ser.out"; FileOutputStream fos = new FileOutputStream(fileName); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(new Person()); oos.flush(); } public void deserializeObject(){ String fileName = "ser.out"; FileInputStream fos = new FileInputStream(fileName); ObjectInputStream oos = new ObjectInputStream(fos); Person p = oos.readObject(); } 上例中我們對一個Person對象定義了序列化和反序列化的操作。但如果Person類是不能序列化的話,,即對不能序列化的類進行序列化操作,,則會拋出 java.io.NotSerializableException異常。 JVM中有一個預(yù)定義的序列化實現(xiàn)機制,,即默認調(diào)用 ObjectOutputStream.defaultWriteObject() 和 ObjectInputStream.defaultReadObject() 來執(zhí)行序列化操作,。如果想自定義序列化的實現(xiàn),則必須在聲明了可序列化的類中實現(xiàn) writeObject()和readObject()方法,。 幾種使用情況 一般在序列化一個類A的時候,,有以下三種情況: [list=3] 對于第一種情況,直接實現(xiàn)Serializable接口即可,。 對于第二種情況,,因為父類B已經(jīng)實現(xiàn)了Serializable接口,故類A無需實現(xiàn)此接口,;如果父類實現(xiàn)了writeObject()和readObject(),,則使用此方法,否則直接使用默認的機制,。 對于第三種情況,,則必須在類A中顯示實現(xiàn)writeObject()和readObject()方法來處理父類B的狀態(tài)信息,;還有一點要特別注意,在父類B中一定要有一個無參的構(gòu)造函數(shù),,這是因為在反序列化的過程中并不會使用聲明為可序列化的類A的任何構(gòu)造函數(shù),,而是會調(diào)用其沒有申明為可序列化的父類B的無參構(gòu)造函數(shù)。 序列化機制的一些問題 [list] 1. 與此實例對象相關(guān)的全部類的元數(shù)據(jù)(metadata)信息,;因為繼承關(guān)系,類A的實例對象也是其任一父類的對象,。因而,需要將整個繼承鏈上的每一個類的元數(shù)據(jù)信息,,按照從父到子的順序依次保存起來,。 2. 類A的描述信息。此描述信息中可能包含有如下這些信息:類的版本ID(version ID),、表示是否自定義了序列化實現(xiàn)機制的標志,、可序列化的屬性的數(shù)目、每個屬性的名字和值,、及其可序列化的父類的描述信息,。 3. 將實例對象作為其每一個超類的實例對象,并將這些數(shù)據(jù)信息都保存起來,。 在RMI等遠程調(diào)用的應(yīng)用中,,每調(diào)用一個方法,都需要傳遞如此多的信息量,;久而久之,,會對系統(tǒng)的性能照成很大的影響。
private static final long serialVersionUID = ALongValue; 這樣,,序列化機制會使用這個值來作為類的版本標識符,從而可以解決不兼容的問題,。但是它卻引入了一個新的問題,,即使一個類作了實質(zhì)性的改變,如增加或刪除了一些可序列化的屬性,,在這種機制下仍然會認為這兩個類是相等的,。 [/list] 一種更好的選擇 作為實現(xiàn)Serializable接口的一種替代方案,實現(xiàn)java.io.Externalizable接口同樣可以標識一個類為可序列化,。 Externalizable接口中定義了以下兩個方法:
public void readExternal(ObjectInput in); public void writeExternal(ObjectOutput out); 這兩個方法的功能與 readObject()和writeObject()方法相同,,任何實現(xiàn)了Externalizable接口的類都需要這實現(xiàn)兩個函數(shù)來定義其序列化機制。 使用Externalizable比使用Serializable有著性能上的提高,。前者序列化一個對象,,所需保存的信息比后者要小,對于后者所需保存的第3個方面的信息,,前者不需要訪問每一個父類并使其保存相關(guān)的狀態(tài)信息,,而只需簡單地調(diào)用類中實現(xiàn)的writeExternal()方法即可。 |
|