我們最常使用的創(chuàng)建對象的方式,就是使用new運(yùn)算符來創(chuàng)建對象,并使用之。比如,如果你有一個Student類,在其構(gòu)造函數(shù)中接受一個人的name,gender和height作為參數(shù),你可以創(chuàng)建一個Student對象,如下所示:
Studentzhangsan=newPerson("張三","男",6.7);
如果你想將對象zhangsan保存到文件中,然后在不使用new運(yùn)算符的情況下將其恢復(fù)到內(nèi)存中,那該怎么做呢?本節(jié)就來討論這個主題內(nèi)容。
首先要明白,將內(nèi)存中的對象轉(zhuǎn)換為字節(jié)序列并將字節(jié)序列存儲在諸如文件的存儲介質(zhì)中的過程稱為對象序列化。你可以將字節(jié)序列存儲到永久存儲器中,例如文件或數(shù)據(jù)庫;還可以通過網(wǎng)絡(luò)傳輸字節(jié)序列。從存儲器中讀取序列化過程產(chǎn)生的字節(jié)序列并將對象恢復(fù)到內(nèi)存中的過程稱為對象反序列化。對象的序列化也稱為對對象進(jìn)行壓縮或編組。對象的反序列化也稱為對對象進(jìn)行復(fù)原或解組??梢詫⑿蛄谢暈閷ο髲膬?nèi)存寫入存儲介質(zhì),將反序列化視為從存儲介質(zhì)將對象讀入內(nèi)存。
完成這兩類工作的是ObjectOutputStream類和ObjectInputStream類
ObjectOutputStream類的對象用于序列化對象。ObjectInputStream類的對象用于反序列化對象。你還可以使用這些類的對象來序列化原始數(shù)據(jù)類型的值,例如int,double,boolean等。
ObjectOutputStream和ObjectInputStream類分別是輸出和輸入流的具體裝飾器類(IO族系類中裝飾器用的非常之多)。但是,它們不是從它們的抽象裝飾器類繼承而來的。它們是從各自的抽象組件類繼承而來的。ObjectOutputStream繼承自O(shè)utputStream,ObjectInputStream繼承自InputStream。貌似這與裝飾模式不一致的。其實,這仍然適合裝飾器模式(這里就不展開了)。
關(guān)于對象序列化,你的需要序列的對象的類,其必須實現(xiàn)Serializable或Externalizable接口,只有這樣了,才能進(jìn)行序列化或反序列化。Serializable接口是一個標(biāo)記接口(沒有任何成員)。如果希望序列化Student類的對象,則需要按如下方式聲明Student類:
序列化時,由Java負(fù)責(zé)從(向)流讀取(寫入)Serializable對象的細(xì)節(jié),而你只需要將對象傳遞給(從)流,調(diào)用相應(yīng)方法把對象寫入(讀取)到流中即可。
若你的類實現(xiàn)了Externalizable接口,可以更好地控制從流中讀取對象和寫入對象。該接口繼承了Serializable接口。聲明如下:
從流中讀取對象時,將調(diào)用readExternal()方法。將對象寫入流時,將調(diào)用writeExternal()方法。但你必須分別編寫邏輯來讀取和寫入readExternal()和writeExternal()方法中的對象字段。實現(xiàn)Externalizable接口的類示例如下:
下面進(jìn)入序列化和反序列化的具體內(nèi)容。
2.對象序列化
要序列化對象,需要執(zhí)行以下步驟:
通過將ObjectInputStream類用作另一個輸入流的裝飾器來創(chuàng)建ObjectInputStream類的對象,該輸入流表示存儲序列化對象的存儲介質(zhì)。例如,要從Student.ser文件中讀取對象,請按如下方式創(chuàng)建對象輸入流:
要從ByteArrayInputStream中讀取對象,請按如下方式創(chuàng)建對象輸出流:
使用ObjectInputStream類的readObject()方法反序列化對象,就像這樣:
注意:確保以調(diào)用writeObject()方法寫入對象相同的順序調(diào)用readObject()方法讀取對象(什么順序?qū)懭刖褪裁错樞蜃x出)。例如,如果你以object-1,float和object-2順序?qū)懭肓巳龡l信息,則必須按相同的順序讀取它們:object-1,float和object-2。
最后,關(guān)閉對象輸入流,如下所示:
清單-3演示了如何從Student.ser文件中讀取對象。確保當(dāng)前目錄中存在Student.ser文件。否則,程序?qū)⒋蛴∫粭l錯誤消息,其中包含此文件的預(yù)期位置。
清單-3.從文件中讀取對象
輸出信息如下(和寫入的對象一致):
4.Externalizable序列化
在前面的部分中,我們介紹了如何序列化和反序列化可序列化對象。在本節(jié)中,我將向你展示如何用Externalizable序列化和反序列化(Externalizable)對象。我修改了Student類來實現(xiàn)Externalizable接口。我將新類命名為StudentExt,如清單-4所示。
Java將分別將對象輸出流和對象輸入流的引用傳遞給StudentExt類的writeExternal()和readExternal()方法。
在writeExternal()方法中,將name和gender字段寫入對象輸出流。請注意,height字段不會寫入對象輸出流。這意味著當(dāng)你從readExternal()方法中讀取流中的對象時,將無法獲得height字段的值。writeUTF()方法用于將字符串(name和gender)寫入對象輸出流。
在readExternal()方法中,可從流中讀取name和gender字段,并在name和gender實例變量中設(shè)置它們。
清單-5和清單-6包含StudentExt對象的序列化和反序列化邏輯。
清單-5.序列化實現(xiàn)了Externalizable接口的StudentExt對象
運(yùn)行程序輸出信息如下:
清單-6.反序列化實現(xiàn)了Externalizable接口的StudentExt對象
輸出結(jié)果如下:
清單-6的輸出演示了在反序列化StudentExt對象后,height字段的值是默認(rèn)值(Double.NaN)。
以下是使用Externalizable接口序列化和反序列化對象的步驟:
1.當(dāng)調(diào)用writeObject()方法來寫入Externalizable對象時,Java(執(zhí)行引擎)會將對象的標(biāo)識寫入到輸出流中,然后調(diào)用其類的writeExternal()方法。你可以在(序列化對象類的)writeExternal()方法中將與對象相關(guān)的數(shù)據(jù)寫入輸出流。如果需要,可以完全控制在此方法中寫入流的對象的相關(guān)數(shù)據(jù)。如果要存儲某些敏感數(shù)據(jù),可能需要先將其加密,然后再將其寫入流中,并在從流中讀取數(shù)據(jù)時對其進(jìn)行解密。
2.當(dāng)調(diào)用readObject()方法讀取Externalizable對象時,Java會從流中讀取對象的標(biāo)識。請注意,對于Externalizable對象,Java僅將對象的標(biāo)識寫入到輸出流,而不是有關(guān)其類定義的任何詳細(xì)信息。Java使用對象類的no-args構(gòu)造函數(shù)來創(chuàng)建對象。這就是你必須為一個Externalizable對象提供一個無參(no-args)構(gòu)造函數(shù)的原因。它調(diào)用對象的readExternal()方法,以便在此可以完成填充或裝配對象的相關(guān)字段值。
對于Serializable對象,JVM僅序列化未聲明為瞬態(tài)的實例變量。
以上就是天津卓眾教育java培訓(xùn)機(jī)構(gòu)小編介紹的“實戰(zhàn)案例:輕松搞定Java兩種序列化機(jī)制”的內(nèi)容,希望對大家有幫助,更多java最新資訊請繼續(xù)關(guān)注天津卓眾教育java培訓(xùn)機(jī)構(gòu)官網(wǎng),每天會有精彩內(nèi)容分享與你。