1. 程式人生 > >【面試】序列化相關-這一篇全瞭解

【面試】序列化相關-這一篇全瞭解

什麼是序列化?有哪些應用場景。

解:

序列化是將物件轉換為可傳輸格式的過程。 是一種資料的持久化手段。

一般廣泛應用於網路傳輸,RMI和RPC等場景中。

什麼是反序列化?

解:

反序列化是序列化的逆操作。序列化是將物件的狀態資訊轉換為可儲存或傳輸的形式的過程。一般是以位元組碼或XML格式傳輸。而位元組碼或XML編碼格式可以還原為完全相等的物件。這個相反的過程稱為反序列化。

Java中如何實現序列化和反序列化。

解:

參考我之前部落格中文章:連結:Java物件的序列化與反序列化-HollisChuang's Blog

Serializable 和 Externalizable 介面有何不同?

解:

類通過實現 java.io.Serializable 介面以啟用其序列化功能。未實現此介面的類將無法使其任何狀態序列化或反序列化。可序列化類的所有子型別本身都是可序列化的。序列化介面沒有方法或欄位,僅用於標識可序列化的語義。

當試圖對一個物件進行序列化的時候,如果遇到不支援 Serializable 介面的物件。在此情況下,將丟擲 NotSerializableException。

如果要序列化的類有父類,要想同時將在父類中定義過的變數持久化下來,那麼父類也應該整合java.io.Serializable介面。

Externalizable繼承了Serializable,該介面中定義了兩個抽象方法:writeExternal()與readExternal()。當使用Externalizable介面來進行序列化與反序列化的時候需要開發人員重寫writeExternal()與readExternal()方法。如果沒有在這兩個方法中定義序列化實現細節,那麼序列化之後,物件內容為空。實現Externalizable介面的類必須要提供一個public的無參的構造器。

所以,實現Externalizable,並實現writeExternal()和readExternal()方法可以指定序列化哪些屬性。

Serializable 介面有幾個方法? 如果沒有方法,那麼為什麼要定義這樣的介面?Java又是如何保證必須實現了Serializable才可以被序列化的呢?

解:

如果一個類想被序列化,需要實現Serializable介面。否則將丟擲NotSerializableException異常。Serializable介面沒有方法或欄位,僅用於標識可序列化的語義。

這是因為,在序列化操作過程中會對型別進行檢查,要求被序列化的類必須屬於Enum、Array和Serializable型別其中的任何一種。

詳情參考:連結:深入分析Java的序列化與反序列化-HollisChuang's Blog

serialVersionUID 有何用途? 如果沒定義會有什麼問題?

解:

序列化是將物件的狀態資訊轉換為可儲存或傳輸的形式的過程。我們都知道,Java物件是儲存在JVM的堆記憶體中的,也就是說,如果JVM堆不存在了,那麼物件也就跟著消失了。而序列化提供了一種方案,可以讓你在即使JVM停機的情況下也能把物件儲存下來的方案。就像我們平時用的U盤一樣。把Java物件序列化成可儲存或傳輸的形式(如二進位制流),比如儲存在檔案中。這樣,當再次需要這個物件的時候,從檔案中讀取出二進位制流,再從二進位制流中反序列化出物件。

但是,虛擬機器是否允許反序列化,不僅取決於類路徑和功能程式碼是否一致,一個非常重要的一點是兩個類的序列化 ID 是否一致,即serialVersionUID要求一致。

在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常,即是InvalidCastException。這樣做是為了保證安全,因為檔案儲存中的內容可能被篡改。

當實現java.io.Serializable介面的類沒有顯式地定義一個serialVersionUID變數時候,Java序列化機制會根據編譯的Class自動生成一個serialVersionUID作序列化版本比較用,這種情況下,如果Class檔案沒有發生變化,就算再編譯多次,serialVersionUID也不會變化的。但是,如果發生了變化,那麼這個檔案對應的serialVersionUID也就會發生變化。

基於以上原理,如果我們一個類實現了Serializable介面,但是沒有定義serialVersionUID,然後序列化。在序列化之後,由於某些原因,我們對該類做了變更,重新啟動應用後,我們相對之前序列化過的物件進行反序列化的話就會報錯。

通過生麼方式可以讓一個類中的某些成員變數不被序列化?

解:

transient 關鍵字的作用是控制變數的序列化,在變數宣告前加上該關鍵字,可以阻止該變數被序列化到檔案中,在被反序列化後,transient 變數的值被設為初始值,如 int 型的是 0,物件型的是 null。

如何自定義序列化策略?

解:

可以通過在被序列化的類中增加writeObject 和 readObject方法來自定義序列化策略。

如果你已經將某個類的例項序列化到磁碟,這時候再往這個類新增新的屬性,那麼反序列化該物件的時候會發生什麼?

解:

只要在類中定義了serialVersionUID,在類中新增屬性不會導致序列化失敗,新增的欄位在反序列化後會為預設值,如null。

在Java中,有哪些好的序列化框架,有什麼好處。

解:Java中常用的序列化框架:

java、kryo、hessian、protostuff、gson、fastjson等。

Kryo:速度快,序列化後體積小;跨語言支援較複雜

Hessian:預設支援跨語言;效率不高

Protostuff:速度快,基於

protobuf;需靜態編譯

Protostuff-Runtime:無需靜態編譯,但序列化前需預先傳入schema;不支援無預設建構函式的類,反序列化時需使用者自己初始化序列化後的物件,其只負責將該物件進行賦值

Java:使用方便,可序列化所有類;速度慢,佔空間效能比較

參考:連結:Java序列化框架效能比較 | 鳥窩

序列化是如何破壞單例模式的?

解:

參考:連結:單例與序列化的那些事兒-HollisChuang's Blog

談談你理解的序列化的安全性問題。

連結:【技術分享】Java 序列化與反序列化安全分析 - SecPulse.COM | 安全脈搏