序列化的過程就是對象寫入字節(jié)流和從字節(jié)流中讀取對象。將對象狀態(tài)轉(zhuǎn)換成字節(jié)流之后,可以用java.io包中的各種字節(jié)流類將其保存到文件中,管道到另一線程中或通過網(wǎng)絡(luò)連接將對象數(shù)據(jù)發(fā)送到另一主機(jī)。對象序列化功能非常簡單、強(qiáng)大,在RMI、Socket、JMS、EJB都有應(yīng)用。對象序列化問題在網(wǎng)絡(luò)編程中并不是最激動人心的課題,但卻相當(dāng)重要,具有許多實用意義。 一:對象序列化可以實現(xiàn)分布式對象。主要應(yīng)用例如:RMI要利用對象序列化運(yùn)行遠(yuǎn)程主機(jī)上的服務(wù),就像在本地機(jī)上運(yùn)行對象時一樣。 二:java對象序列化不僅保留一個對象的數(shù)據(jù),而且遞歸保存對象引用的每個對象的數(shù)據(jù)??梢詫⒄麄€對象層次寫入字節(jié)流中,可以保存在文件中或在網(wǎng)絡(luò)連接上傳遞。利用對象序列化可以進(jìn)行對象的“深復(fù)制”,即復(fù)制對象本身及引用的對象本身。序列化一個對象可能得到整個對象序列。 從上面的敘述中,我們知道了對象序列化是java編程中的必備武器,那么讓我們從基礎(chǔ)開始,好好學(xué)習(xí)一下它的機(jī)制和用法。 java序列化比較簡單,通常不需要編寫保存和恢復(fù)對象狀態(tài)的定制代碼。實現(xiàn)java.io.Serializable接口的類對象可以轉(zhuǎn)換成字節(jié)流或從字節(jié)流恢復(fù),不需要在類中增加任何代碼。只有極少數(shù)情況下才需要定制代碼保存或恢復(fù)對象狀態(tài)。這里要注意:不是每個類都可序列化,有些類是不能序列化的,例如涉及線程的類與特定JVM有非常復(fù)雜的關(guān)系。 序列化機(jī)制: 序列化分為兩大部分:序列化和反序列化。序列化是這個過程的第一部分,將數(shù)據(jù)分解成字節(jié)流,以便存儲在文件中或在網(wǎng)絡(luò)上傳輸。反序列化就是打開字節(jié)流并重構(gòu)對象。對象序列化不僅要將基本數(shù)據(jù)類型轉(zhuǎn)換成字節(jié)表示,有時還要恢復(fù)數(shù)據(jù)?;謴?fù)數(shù)據(jù)要求有恢復(fù)數(shù)據(jù)的對象實例。ObjectOutputStream中的序列化過程與字節(jié)流連接,包括對象類型和版本信息。反序列化時,JVM用頭信息生成對象實例,然后將對象字節(jié)流中的數(shù)據(jù)復(fù)制到對象數(shù)據(jù)成員中。下面我們分兩大部分來闡述: 處理對象流: (序列化過程和反序列化過程) java.io包有兩個序列化對象的類。ObjectOutputStream負(fù)責(zé)將對象寫入字節(jié)流,ObjectInputStream從字節(jié)流重構(gòu)對象。 我們先了解ObjectOutputStream類吧。ObjectOutputStream類擴(kuò)展DataOutput接口。 writeObject()方法是最重要的方法,用于對象序列化。如果對象包含其他對象的引用,則writeObject()方法遞歸序列化這些對象。每個ObjectOutputStream維護(hù)序列化的對象引用表,防止發(fā)送同一對象的多個拷貝。(這點很重要)由于writeObject()可以序列化整組交叉引用的對象,因此同一ObjectOutputStream實例可能不小心被請求序列化同一對象。這時,進(jìn)行反引用序列化,而不是再次寫入對象字節(jié)流。 下面,讓我們從例子中來了解ObjectOutputStream這個類吧。 // 序列化 today‘s date 到一個文件中. 例子如下: //從文件中反序列化 string 對象和 date 對象 序列化通常可以自動完成,但有時可能要對這個過程進(jìn)行控制。java可以將類聲明為serializable,但仍可手工控制聲明為static或transient的數(shù)據(jù)成員。 例子:一個非常簡單的序列化類。 public class simpleSerializableClass implements Serializable{ 關(guān)于如何使用定制序列化的部分代碼如下: //重寫writeObject()方法以便處理transient的成員。 public void writeObject(ObjectOutputStream outputStream) throws IOException{ outputStream.writeObject(oSocket.getInetAddress()); private void readObject(ObjectInputStream inputStream) throws IOException,ClassNotFoundException{ 如果一個類要完全負(fù)責(zé)自己的序列化,則實現(xiàn)Externalizable接口而不是Serializable接口。Externalizable接口定義包括兩個方法writeExternal()與readExternal()。利用這些方法可以控制對象數(shù)據(jù)成員如何寫入字節(jié)流.類實現(xiàn)Externalizable時,頭寫入對象流中,然后類完全負(fù)責(zé)序列化和恢復(fù)數(shù)據(jù)成員,除了頭以外,根本沒有自動序列化。這里要注意了。聲明類實現(xiàn)Externalizable接口會有重大的安全風(fēng)險。writeExternal()與readExternal()方法聲明為public,惡意類可以用這些方法讀取和寫入對象數(shù)據(jù)。如果對象包含敏感信息,則要格外小心。這包括使用安全套接或加密整個字節(jié)流。到此為至,我們學(xué)習(xí)了序列化的基礎(chǔ)部分知識。 |