為什么等待和通知是在Object類而不是Thread中聲明的?
一個棘手的Java問題,如果Java編程語言不是你設(shè)計的,你怎么能回答這個問題呢。Java編程的常識和深入了解有助于回答這種棘手的Java核心方面的面試問題。
為什么wait,notify和notifyAll是在Object類中定義的而不是在Thread類中定義
這是有名的Java面試問題,招2~4年經(jīng)驗的到高級Java開發(fā)人員面試都可能碰到。
這個問題的好在它能反映了面試者對等待通知機(jī)制的了解,以及他對此主題的理解是否明確。就像為什么Java中不支持多繼承或者為什么String在Java中是final的問題一樣,這個問題也可能有多個答案。
為什么在Object類中定義wait和notify方法,每個人都能說出一些理由。從我的面試經(jīng)驗來看,wait和nofity仍然是大多數(shù)Java程序員最困惑的,特別是2到3年的開發(fā)人員,如果他們要求使用wait和notify,他們會很困惑。因此,如果你去參加Java面試,請確保對wait和notify機(jī)制有充分的了解,并且可以輕松地使用wait來編寫代碼,并通過生產(chǎn)者-消費者問題或?qū)崿F(xiàn)阻塞隊列等了解通知的機(jī)制。
為什么等待和通知需要從同步塊或方法中調(diào)用,以及Java中的wait,sleep和yield方法之間的差異,如果你還沒有讀過,你會覺得有趣。為何wait,notify和notifyAll屬于Object類?為什么它們不應(yīng)該在Thread類中?以下是我認(rèn)為有意義的一些想法:
1)wait和notify不僅僅是普通方法或同步工具,更重要的是它們是Java中兩個線程之間的通信機(jī)制。對語言設(shè)計者而言,如果不能通過Java關(guān)鍵字(例如synchronized)實現(xiàn)通信此機(jī)制,同時又要確保這個機(jī)制對每個對象可用,那么Object類則是的正確聲明位置。記住同步和等待通知是兩個不同的領(lǐng)域,不要把它們看成是相同的或相關(guān)的。同步是提供互斥并確保Java類的線程安全,而wait和notify是兩個線程之間的通信機(jī)制。
2)每個對象都可上鎖,這是在Object類而不是Thread類中聲明wait和notify的另一個原因。
3)在Java中為了進(jìn)入代碼的臨界區(qū),線程需要鎖定并等待鎖定,他們不知道哪些線程持有鎖,而只是知道鎖被某個線程持有,并且他們應(yīng)該等待取得鎖,而不是去了解哪個線程在同步塊內(nèi),并請求它們釋放鎖定。
4)Java是基于Hoare的監(jiān)視器的思想。在Java中,所有對象都有一個監(jiān)視器。
線程在監(jiān)視器上等待,為執(zhí)行等待,我們需要2個參數(shù):
一個線程
一個監(jiān)視器(任何對象)
在Java設(shè)計中,線程不能被指定,它總是運行當(dāng)前代碼的線程。但是,我們可以指定監(jiān)視器(這是我們稱之為等待的對象)。這是一個很好的設(shè)計,因為如果我們可以讓任何其他線程在所需的監(jiān)視器上等待,這將導(dǎo)致“入侵”,導(dǎo)致在設(shè)計并發(fā)程序時會遇到困難。請記住,在Java中,所有在另一個線程的執(zhí)行中侵入的操作都被棄用了(例如stop方法)。
如果你的Serializable類包含一個不可序列化的成員,會發(fā)生什么?你是如何解決的?
任何序列化該類的嘗試都會因NotSerializableException而失敗,但這可以通過在Java中為static設(shè)置瞬態(tài)(trancient)變量來輕松解決。
Java序列化相關(guān)的常見問題
Java序列化是一個重要概念,但它很少用作持久性解決方案,開發(fā)人員大多忽略了Java序列化API。根據(jù)我的經(jīng)驗,Java序列化在任何Java核心內(nèi)容面試中都是一個相當(dāng)重要的話題,在幾乎所有的網(wǎng)面試中,我都遇到過一兩個Java序列化問題,我看過一次面試,在問幾個關(guān)于序列化的問題之后候選人開始感到不自在,因為缺乏這方面的經(jīng)驗。
他們不知道如何在Java中序列化對象,或者他們不熟悉任何Java示例來解釋序列化,忘記了諸如序列化在Java中如何工作,什么是標(biāo)記接口,標(biāo)記接口的目的是什么,瞬態(tài)變量和可變變量之間的差異,可序列化接口具有多少種方法,在Java中,Serializable和Externalizable有什么區(qū)別,或者在引入注解之后,為什么不用@Serializable注解或替換Serializalbe接口。
在本文中,我們將從初學(xué)者和高級別進(jìn)行提問,這對新手和具有多年Java開發(fā)經(jīng)驗的高級開發(fā)人員同樣有益。
關(guān)于Java序列化的10個面試問題
大多數(shù)商業(yè)項目使用數(shù)據(jù)庫或內(nèi)存映射文件或只是普通文件,來滿足持久性要求,只有很少的項目依賴于Java中的序列化過程。無論如何,這篇文章不是Java序列化教程或如何序列化在Java的對象,但有關(guān)序列化機(jī)制和序列化API的面試問題,這是值得去任何Java面試前先看看以免讓一些未知的內(nèi)容驚到自己。
對于那些不熟悉Java序列化的人,Java序列化是用來通過將對象的狀態(tài)存儲到帶有.ser擴(kuò)展名的文件來序列化Java中的對象的過程,并且可以通過這個文件恢復(fù)重建Java對象狀態(tài),這個逆過程稱為deserialization。
什么是Java序列化
序列化是把對象改成可以存到磁盤或通過網(wǎng)絡(luò)發(fā)送到其他運行中的Java虛擬機(jī)的二進(jìn)制格式的過程,并可以通過反序列化恢復(fù)對象狀態(tài).Java序列化API給開發(fā)人員提供了一個標(biāo)準(zhǔn)機(jī)制,通過java.io.Serializable和java.io.Externalizable接口,ObjectInputStream及ObjectOutputStream處理對象序列化.Java程序員可自由選擇基于類結(jié)構(gòu)的標(biāo)準(zhǔn)序列化或是他們自定義的二進(jìn)制格式,通常認(rèn)為后者才是最佳實踐,因為序列化的二進(jìn)制文件格式成為類輸出API的一部分,可能破壞Java中私有和包可見的屬性的封裝.
如何序列化
讓Java中的類可以序列化很簡單.你的Java類只需要實現(xiàn)java.io.Serializable接口,JVM就會把Object對象按默認(rèn)格式序列化.讓一個類是可序列化的需要有意為之.類可序列會可能為是一個長期代價,可能會因此而限制你修改或改變其實現(xiàn).當(dāng)你通過實現(xiàn)添加接口來更改類的結(jié)構(gòu)時,添加或刪除任何字段可能會破壞默認(rèn)序列化,這可以通過自定義二進(jìn)制格式使不兼容的可能性最小化,但仍需要大量的努力來確保向后兼容性。序列化如何限制你更改類的能力的一個示例是SerialVersionUID。
如果不顯式聲明SerialVersionUID,則JVM會根據(jù)類結(jié)構(gòu)生成其結(jié)構(gòu),該結(jié)構(gòu)依賴于類實現(xiàn)接口和可能更改的其他幾個因素。假設(shè)你新版本的類文件實現(xiàn)的另一個接口,JVM將生成一個不同的SerialVersionUID的,當(dāng)你嘗試加載舊版本的程序序列化的舊對象時,你將獲得無效類異常InvalidClassException。
問題1)Java中的可序列化接口和可外部接口之間的區(qū)別是什么?
這是Java序列化訪談中最常問的問題。下面是我的版本Externalizable給我們提供writeExternal()和readExternal()方法,這讓我們靈活地控制Java序列化機(jī)制,而不是依賴于Java的默認(rèn)序列化。正確實現(xiàn)Externalizable接口可以顯著提高應(yīng)用程序的性能。
問題2)可序列化的方法有多少?如果沒有方法,那么可序列化接口的用途是什么?
可序列化Serializalbe接口存在于java.io包中,構(gòu)成了Java序列化機(jī)制的核心。它沒有任何方法,在Java中也稱為標(biāo)記接口。當(dāng)類實現(xiàn)java.io.Serializable接口時,它將在Java中變得可序列化,并指示編譯器使用Java序列化機(jī)制序列化此對象。
問題3)什么是serialVersionUID?如果你不定義這個,會發(fā)生什么?
我最喜歡的關(guān)于Java序列化的問題面試問題之一。serialVersionUID是一個privatestaticfinallong型ID,當(dāng)它被印在對象上時,它通常是對象的哈希碼,你可以使用serialver這個JDK工具來查看序列化對象的serialVersionUID。SerialVerionUID用于對象的版本控制。也可以在類文件中指定serialVersionUID。不指定serialVersionUID的后果是,當(dāng)你添加或修改類中的任何字段時,則已序列化類將無法恢復(fù),因為為新類和舊序列化對象生成的serialVersionUID將有所不同。Java序列化過程依賴于正確的序列化對象恢復(fù)狀態(tài)的,,并在序列化對象序列版本不匹配的情況下引發(fā)java.io.InvalidClassException無效類異常,了解有關(guān)serialVersionUID詳細(xì)信息,請參閱這篇文章,需要FQ。
問題4)序列化時,你希望某些成員不要序列化?你如何實現(xiàn)它?
另一個經(jīng)常被問到的序列化面試問題。這也是一些時候也問,如什么是瞬態(tài)trasient變量,瞬態(tài)和靜態(tài)變量會不會得到序列化等,所以,如果你不希望任何字段是對象的狀態(tài)的一部分,然后聲明它靜態(tài)或瞬態(tài)根據(jù)你的需要,這樣就不會是在Java序列化過程中被包含在內(nèi)。
問題5)如果類中的一個成員未實現(xiàn)可序列化接口,會發(fā)生什么情況?
關(guān)于Java序列化過程的一個簡單問題。如果嘗試序列化實現(xiàn)可序列化的類的對象,但該對象包含對不可序列化類的引用,則在運行時將引發(fā)不可序列化異常NotSerializableException,這就是為什么我始終將一個可序列化警報(在我的代碼注釋部分中),代碼注釋最佳實踐之一,指示開發(fā)人員記住這一事實,在可序列化類中添加新字段時要注意。
問題6)如果類是可序列化的,但其超類不是,則反序列化后從超級類繼承的實例變量的狀態(tài)如何?
Java序列化過程僅在對象層次都是可序列化結(jié)構(gòu)中繼續(xù),即實現(xiàn)Java中的可序列化接口,并且從超級類繼承的實例變量的值將通過調(diào)用構(gòu)造函數(shù)初始化,在反序列化過程中不可序列化的超級類。一旦構(gòu)造函數(shù)鏈接將啟動,就不可能停止,因此,即使層次結(jié)構(gòu)中較高的類實現(xiàn)可序列化接口,也將執(zhí)行構(gòu)造函數(shù)。正如你從陳述中看到的,這個序列化面試問題看起來非常棘手和有難度,但如果你熟悉關(guān)鍵概念,則并不難。
問題7)是否可以自定義序列化過程,或者是否可以覆蓋Java中的默認(rèn)序列化過程?
答案是肯定的,你可以。我們都知道,對于序列化一個對象需調(diào)用ObjectOutputStream.writeObject(saveThisObject),并用ObjectInputStream.readObject()讀取對象,但Java虛擬機(jī)為你提供的還有一件事,是定義這兩個方法。如果在類中定義這兩種方法,則JVM將調(diào)用這兩種方法,而不是應(yīng)用默認(rèn)序列化機(jī)制。你可以在此處通過執(zhí)行任何類型的預(yù)處理或后處理任務(wù)來自定義對象序列化和反序列化的行為。
需要注意的重要一點是要聲明這些方法為私有方法,以避免被繼承、重寫或重載。由于只有Java虛擬機(jī)可以調(diào)用類的私有方法,你的類的完整性會得到保留,并且Java序列化將正常工作。在我看來,這是在任何Java序列化面試中可以問的最好問題之一,一個很好的后續(xù)問題是,為什么要為你的對象提供自定義序列化表單?
問題8)假設(shè)新類的超級類實現(xiàn)可序列化接口,如何避免新類被序列化?
在Java序列化中一個棘手的面試問題。如果類的Super類已經(jīng)在Java中實現(xiàn)了可序列化接口,那么它在Java中已經(jīng)可以序列化,因為你不能取消接口,它不可能真正使它無法序列化類,但是有一種方法可以避免新類序列化。為了避免Java序列化,你需要在類中實現(xiàn)writeObject()和readObject()方法,并且需要從該方法引發(fā)不序列化異常NotSerializableException。這是自定義Java序列化過程的另一個好處,如上述序列化面試問題中所述,并且通常隨著面試進(jìn)度,它作為后續(xù)問題提出。
問題9)在Java中的序列化和反序列化過程中使用哪些方法?
這是很常見的面試問題,在序列化基本上面試官試圖知道:你是否熟悉readObject()的用法、writeObject()、readExternal()和writeExternal()。Java序列化由java.io.ObjectOutputStream類完成。該類是一個篩選器流,它封裝在較低級別的字節(jié)流中,以處理序列化機(jī)制。要通過序列化機(jī)制存儲任何對象,我們調(diào)用ObjectOutputStream.writeObject(savethisobject),并反序列化該對象,我們稱之為ObjectInputStream.readObject()方法。調(diào)用以writeObject()方法在java中觸發(fā)序列化過程。關(guān)于readObject()方法,需要注意的一點很重要一點是,它用于從持久性讀取字節(jié),并從這些字節(jié)創(chuàng)建對象,并返回一個對象,該對象需要類型強(qiáng)制轉(zhuǎn)換為正確的類型。
問題10)假設(shè)你有一個類,它序列化并存儲在持久性中,然后修改了該類以添加新字段。如果對已序列化的對象進(jìn)行反序列化,會發(fā)生什么情況?
這取決于類是否具有其自己的serialVersionUID。正如我們從上面的問題知道,如果我們不提供serialVersionUID,則Java編譯器將生成它,通常它等于對象的哈希代碼。通過添加任何新字段,有可能為該類新版本生成的新serialVersionUID與已序列化的對象不同,在這種情況下,Java序列化API將引發(fā)java.io.InvalidClassException,因此建議在代碼中擁有自己的serialVersionUID,并確保在單個類中始終保持不變。
11)Java序列化機(jī)制中的兼容更改和不兼容更改是什么?
真正的挑戰(zhàn)在于通過添加任何字段、方法或刪除任何字段或方法來更改類結(jié)構(gòu),方法是使用已序列化的對象。根據(jù)Java序列化規(guī)范,添加任何字段或方法都面臨兼容的更改和更改類層次結(jié)構(gòu)或取消實現(xiàn)的可序列化接口,有些接口在非兼容更改下。對于兼容和非兼容更改的完整列表,我建議閱讀Java序列化規(guī)范。
12)我們可以通過網(wǎng)絡(luò)傳輸一個序列化的對象嗎?
是的,你可以通過網(wǎng)絡(luò)傳輸序列化對象,因為Java序列化對象仍以字節(jié)的形式保留,字節(jié)可以通過網(wǎng)絡(luò)發(fā)送。你還可以將序列化對象存儲在磁盤或數(shù)據(jù)庫中作為Blob。
13)在Java序列化期間,哪些變量未序列化?
這個問題問得不同,但目的還是一樣的,Java開發(fā)人員是否知道靜態(tài)和瞬態(tài)變量的細(xì)節(jié)。由于靜態(tài)變量屬于類,而不是對象,因此它們不是對象狀態(tài)的一部分,因此在Java序列化過程中不會保存它們。由于Java序列化僅保留對象的狀態(tài),而不是對象本身。瞬態(tài)變量也不包含在Java序列化過程中,并且不是對象的序列化狀態(tài)的一部分。在提出這個問題之后,面試官會詢問后續(xù)內(nèi)容,如果你不存儲這些變量的值,那么一旦對這些對象進(jìn)行反序列化并重新創(chuàng)建這些變量,這些變量的價值是多少?這是你們要考慮的。
以上就是長沙達(dá)內(nèi)教育Java培訓(xùn)機(jī)構(gòu)小編介紹的“最新java面試題及答案整理”的內(nèi)容,希望對大家有幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務(wù)。
相關(guān)推薦
最新最全java面試題及答案(初級到高級)
史上最全的中高級JAVA工程師面試題及答案匯總
Java高級開發(fā)工程師面試題
2019史上最全java面試題題庫大全800題
哪有資深java工程師面試題