Java中的可序列化接口和可外部接口之間的區(qū)別是什么?
下面是我的版本Externalizable給我們提供writeExternal()和readExternal()方法,這讓我們靈活地控制Java序列化機(jī)制,而不是依賴于Java的默認(rèn)序列化。正確實(shí)現(xiàn)Externalizable接口可以顯著提高應(yīng)用程序的性能。
可序列化的方法有多少?如果沒有方法,那么可序列化接口的用途是什么?
可序列化Serializalbe接口存在于java.io包中,構(gòu)成了Java序列化機(jī)制的核心。它沒有任何方法,在Java中也稱為標(biāo)記接口。當(dāng)類實(shí)現(xiàn)java.io.Serializable接口時(shí),它將在Java中變得可序列化,并指示編譯器使用Java序列化機(jī)制序列化此對象。
什么是serialVersionUID?如果你不定義這個(gè),會發(fā)生什么?
serialVersionUID是一個(gè)privatestaticfinallong型ID,當(dāng)它被印在對象上時(shí),它通常是對象的哈希碼,你可以使用serialver這個(gè)JDK工具來查看序列化對象的serialVersionUID。SerialVerionUID用于對象的版本控制。也可以在類文件中指定serialVersionUID。不指定serialVersionUID的后果是,當(dāng)你添加或修改類中的任何字段時(shí),則已序列化類將無法恢復(fù),因?yàn)闉樾骂惡团f序列化對象生成的serialVersionUID將有所不同。
序列化時(shí),你希望某些成員不要序列化?你如何實(shí)現(xiàn)它?
這也是一些時(shí)候也問,如什么是瞬態(tài)trasient變量,瞬態(tài)和靜態(tài)變量會不會得到序列化等,所以,如果你不希望任何字段是對象的狀態(tài)的一部分,然后聲明它靜態(tài)或瞬態(tài)根據(jù)你的需要,這樣就不會是在Java序列化過程中被包含在內(nèi)。
如果類中的一個(gè)成員未實(shí)現(xiàn)可序列化接口,會發(fā)生什么情況?
如果嘗試序列化實(shí)現(xiàn)可序列化的類的對象,但該對象包含對不可序列化類的引用,則在運(yùn)行時(shí)將引發(fā)不可序列化異常NotSerializableException,這就是為什么我始終將一個(gè)可序列化警報(bào)(在我的代碼注釋部分中),代碼注釋最佳實(shí)踐之一,指示開發(fā)人員記住這一事實(shí),在可序列化類中添加新字段時(shí)要注意。
如果類是可序列化的,但其超類不是,則反序列化后從超級類繼承的實(shí)例變量的狀態(tài)如何?
Java序列化過程僅在對象層次都是可序列化結(jié)構(gòu)中繼續(xù),即實(shí)現(xiàn)Java中的可序列化接口,并且從超級類繼承的實(shí)例變量的值將通過調(diào)用構(gòu)造函數(shù)初始化,在反序列化過程中不可序列化的超級類。一旦構(gòu)造函數(shù)鏈接將啟動,就不可能停止,因此,即使層次結(jié)構(gòu)中較高的類實(shí)現(xiàn)可序列化接口,也將執(zhí)行構(gòu)造函數(shù)。
是否可以自定義序列化過程,或者是否可以覆蓋Java中的默認(rèn)序列化過程?
答案是肯定的,你可以。對于序列化一個(gè)對象需調(diào)用ObjectOutputStream.writeObject(saveThisObject),并用ObjectInputStream.readObject()讀取對象,但Java虛擬機(jī)為你提供的還有一件事,是定義這兩個(gè)方法。如果在類中定義這兩種方法,則JVM將調(diào)用這兩種方法,而不是應(yīng)用默認(rèn)序列化機(jī)制。你可以在此處通過執(zhí)行任何類型的預(yù)處理或后處理任務(wù)來自定義對象序列化和反序列化的行為。
需要注意的重要一點(diǎn)是要聲明這些方法為私有方法,以避免被繼承、重寫或重載。由于只有Java虛擬機(jī)可以調(diào)用類的私有方法,你的類的完整性會得到保留,并且Java序列化將正常工作。在我看來,這是在任何Java序列化面試中可以問的最好問題之一,一個(gè)很好的后續(xù)問題是,為什么要為你的對象提供自定義序列化表單?
假設(shè)新類的超級類實(shí)現(xiàn)可序列化接口,如何避免新類被序列化?
如果類的Super類已經(jīng)在Java中實(shí)現(xiàn)了可序列化接口,那么它在Java中已經(jīng)可以序列化,因?yàn)槟悴荒苋∠涌?它不可能真正使它無法序列化類,但是有一種方法可以避免新類序列化。為了避免Java序列化,你需要在類中實(shí)現(xiàn)writeObject()和readObject()方法,并且需要從該方法引發(fā)不序列化異常NotSerializableException。這是自定義Java序列化過程的另一個(gè)好處,如上述序列化面試問題中所述,并且通常隨著面試進(jìn)度,它作為后續(xù)問題提出。
在Java中的序列化和反序列化過程中使用哪些方法?
在序列化基本上面試官試圖知道:你是否熟悉readObject()的用法、writeObject()、readExternal()和writeExternal()。Java序列化由java.io.ObjectOutputStream類完成。該類是一個(gè)篩選器流,它封裝在較低級別的字節(jié)流中,以處理序列化機(jī)制。要通過序列化機(jī)制存儲任何對象,我們調(diào)用ObjectOutputStream.writeObject(savethisobject),并反序列化該對象,我們稱之為ObjectInputStream.readObject()方法。調(diào)用以writeObject()方法在java中觸發(fā)序列化過程。關(guān)于readObject()方法,需要注意的一點(diǎn)很重要一點(diǎn)是,它用于從持久性讀取字節(jié),并從這些字節(jié)創(chuàng)建對象,并返回一個(gè)對象,該對象需要類型強(qiáng)制轉(zhuǎn)換為正確的類型。
假設(shè)你有一個(gè)類,它序列化并存儲在持久性中,然后修改了該類以添加新字段。如果對已序列化的對象進(jìn)行反序列化,會發(fā)生什么情況?
這取決于類是否具有其自己的serialVersionUID。正如我們從上面的問題知道,如果我們不提供serialVersionUID,則Java編譯器將生成它,通常它等于對象的哈希代碼。通過添加任何新字段,有可能為該類新版本生成的新serialVersionUID與已序列化的對象不同,在這種情況下,Java序列化API將引發(fā)java.io.InvalidClassException,因此建議在代碼中擁有自己的serialVersionUID,并確保在單個(gè)類中始終保持不變。
Java序列化機(jī)制中的兼容更改和不兼容更改是什么?
真正的挑戰(zhàn)在于通過添加任何字段、方法或刪除任何字段或方法來更改類結(jié)構(gòu),方法是使用已序列化的對象。根據(jù)Java序列化規(guī)范,添加任何字段或方法都面臨兼容的更改和更改類層次結(jié)構(gòu)或取消實(shí)現(xiàn)的可序列化接口,有些接口在非兼容更改下。對于兼容和非兼容更改的完整列表,我建議閱讀Java序列化規(guī)范。
我們可以通過網(wǎng)絡(luò)傳輸一個(gè)序列化的對象嗎?
是的,你可以通過網(wǎng)絡(luò)傳輸序列化對象,因?yàn)镴ava序列化對象仍以字節(jié)的形式保留,字節(jié)可以通過網(wǎng)絡(luò)發(fā)送。你還可以將序列化對象存儲在磁盤或數(shù)據(jù)庫中作為Blob。
在Java序列化期間,哪些變量未序列化?
這個(gè)問題問得不同,但目的還是一樣的,Java開發(fā)人員是否知道靜態(tài)和瞬態(tài)變量的細(xì)節(jié)。由于靜態(tài)變量屬于類,而不是對象,因此它們不是對象狀態(tài)的一部分,因此在Java序列化過程中不會保存它們。由于Java序列化僅保留對象的狀態(tài),而不是對象本身。瞬態(tài)變量也不包含在Java序列化過程中,并且不是對象的序列化狀態(tài)的一部分。在提出這個(gè)問題之后,面試官會詢問后續(xù)內(nèi)容,如果你不存儲這些變量的值,那么一旦對這些對象進(jìn)行反序列化并重新創(chuàng)建這些變量,這些變量的價(jià)值是多少?這是你們要考慮的。