天天看點

Java Serializable接口實作問題

JavaBeans

      要了解Serializable接口之前先來了解最基本的JavaBeans。JavaBeans為我們提供了最簡單的Java類實體,這些實體伴随着業務在應用中來回穿梭。JavaBeans是Java中一種特殊的類,可以将多個對象封裝到一個對象(bean)中。特點是可序列化,提供無參構造器,提供getter方法和setter方法通路對象的屬性。名稱中的“Bean”是用于Java的可重用軟體元件的慣用叫法。

   JavaBeans優點:

   (1) Bean可以控制它的屬性、事件和方法是否暴露給其他程式。

   (2) Bean可以接收來自其他對象的事件,也可以産生事件給其他對象。

   (3) 有軟體可用來配置Bean。

   (4) Bean的屬性可以被序列化,以供日後重用。

   JavaBeans規範:

    要成為JavaBean類,則必須遵循關于命名、構造器、方法的特定規範。有了這些規範,才能有可以使用、複用、替代和連接配接JavaBeans的工具。規範如下:

    (1)有一個公有的無參構造器。

    (2) 屬性可以通過get、set、is(可以替代get,用在布爾型屬性上)方法或遵循特定命名規範的其他方法通路。

    (3)可序列化。

  JavaBean例子:

Java Serializable接口實作問題

注:以上使用的是預設的serialVersionUID,會被警告,因為這樣不符合Java的性能要求。

Serializable

        Java類通過實作 java.io.Serializable 接口以啟用其序列化功能。未實作此接口的類将無法使其任何狀态序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用于辨別可序列化的語義。要允許不可序列化類的子類型序列化,可以假定該子類型負責儲存和還原超類型的公用 (public)、受保護的 (protected) 和(如果可通路)包 (package) 字段的狀态。僅在子類型擴充的類(父類)有一個可通路的無參數構造方法來初始化該類的狀态時,才可以假定子類型有此責任。如果不是這種情況,則聲明一個類為可序列化類是錯誤的。該錯誤将在運作時檢測到。在反序列化過程中,将使用該類的公用或受保護的無參數構造方法初始化不可序列化類的字段。可序列化的子類必須能夠通路無參數的構造方法。可序列化子類的字段将從該流中還原。

     serialVersionUID

      序列化運作時使用一個稱為 serialVersionUID 的版本号與每個可序列化類相關聯,該序列号在反序列化過程中用于驗證序列化對象的發送者和接收者是否為該對象加載了與序列化相容的類。如果接收者加載的該對象的類的 serialVersionUID 與對應的發送者的類的版本号不同,則反序列化将會導緻 InvalidClassException。可序列化類可以通過聲明名為 "serialVersionUID" 的字段(該字段必須是靜态 (static)、最終 (final) 的 long 型字段)顯式聲明其自己的 serialVersionUID:

Java Serializable接口實作問題

       如果可序列化類未顯式聲明 serialVersionUID,則序列化運作時将基于該類的各個方面計算該類的預設 serialVersionUID 值,如“Java(TM) 對象序列化規範”中所述。不過,強烈建議 所有可序列化類都顯式聲明 serialVersionUID 值,原因計算預設的 serialVersionUID 對類的詳細資訊具有較高的敏感性,根據編譯器實作的不同可能千差萬别,這樣在反序列化過程中可能會導緻意外的 InvalidClassException。是以,為保證 serialVersionUID 值跨不同 java 編譯器實作的一緻性,序列化類必須聲明一個明确的 serialVersionUID 值。還強烈建議使用 private 修改器顯示聲明 serialVersionUID(如果可能),原因是這種聲明僅應用于立即聲明類 -- serialVersionUID 字段作為繼承成員沒有用處。

        實作java.io.Serializable 接口的類是可序列化的。沒有實作此接口的類将不能使它們的任一狀态被序列化或逆序列化。

        序列化類的所有子類本身都是可序列化的。這個序列化接口沒有任何方法和域,僅用于辨別序列化的語意。允許非序列化類的子類型序列化,子類型可以假定負責儲存和恢複父類型的公有的、保護的和(如果可通路)包的域的狀态。隻要該類(即父類)有一個無參構造子,可初始化它的狀态,那麼子類型就可承擔上述職責;如果該類沒有無參構造函數,在這種情況下申明一個可序列化的類是一個錯誤。此錯誤将在運作時被檢測。

      任何類型隻要實作了Serializable接口,就可以被儲存到檔案中,或者作為資料流通過網絡發送到别的地方。也可以用管道來傳輸到系統的其他程式中。

提供案例

      如下所示的兩個方法,都沒有問題,實質的問題在他們傳遞的SortingQualityCollection對象(JavaBean):

Java Serializable接口實作問題
Java Serializable接口實作問題

        在程式運作時,save和update方法都可以執行成功,但是永遠傳回的狀态都是false,這一點很讓人費解,在debug後發現使用Spring AOP的使用是不能夠成功的,另外最直接的錯誤是報出cglib的資源不存在。很疑惑的是,程式執行成功了update和insert的語句在背景也列印出來了,資料庫中記錄正确,由于傳回的是false導緻程式中業務處理始終不能往下進行。

       對比過程:檢視資料對象和資料庫字段是否一緻、另外JavaBean是否規範。但做完這些事情還是無濟于事,後來問題隻能鎖定在JavaBean上。JavaBean是通過工具生成的,serialVersionUID如下:

Java Serializable接口實作問題

      于是重新生成serialVersionUID,新的serialVersionUID如下:

Java Serializable接口實作問題

    重新運作程式,程式執行成功,由此可見serialVersionUID的聲明很重要,最好在寫完JavaBean之後手動生成之,并且顯示聲明。