Serializable介面作用解析

來源:互聯網
上載者:User

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

  要允許不可序列化類別的子類型序列化,可以假定該子類型負責儲存和還原超類型的公用 (public)、受保護的 (protected) 和(如果可訪問)包 (package) 欄位的狀態。僅在子類型擴充的類有一個可訪問的無參數構造方法來初始化該類的狀態時,才可以假定子類型有此責任。如果不是這種情況,則聲明一個類為可序列化類別是錯誤的。該錯誤將在運行時檢測到。

  在還原序列化過程中,將使用該類的公用或受保護的無參數構造方法初始化不可序列化類別的欄位。可序列化的子類必須能夠訪問無參數的構造方法。可序列化子類的欄位將從該流中還原。

  當遍曆一個圖形時,可能會遇到不支援可序列化介面的對象。在此情況下,將拋出 NotSerializableException,並將標識不可序列化對象的類。

  在序列化和還原序列化過程中需要特殊處理的類必須使用下列準確簽名來實現特殊方法:

  private void writeObject(java.io.ObjectOutputStream out)

  throws IOException

  private void readObject(java.io.ObjectInputStream in)

  throws IOException, ClassNotFoundException;

  writeObject 方法負責寫入特定類的對象的狀態,以便相應的 readObject 方法可以還原它。通過調用 out.defaultWriteObject 可以調用儲存 Object 的欄位的預設機制。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基礎資料型別 (Elementary Data Type)的方法將各個欄位寫入 ObjectOutputStream 來儲存的。

  readObject 方法負責從流中讀取並還原類欄位。它可以調用 in.defaultReadObject 來調用預設機制,以還原對象的非靜態和非瞬態欄位。defaultReadObject 方法使用流中的資訊來分配流中通過當前對象中相應命名欄位儲存的對象的欄位。這用於處理類發展後需要添加新欄位的情形。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基礎資料型別 (Elementary Data Type)的方法將各個欄位寫入 ObjectOutputStream 來儲存的。

  將對象寫入流時需要指定要使用的替代對象的可序列化類別,應使用準確的簽名來實現此特殊方法:

  ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

  此 writeReplace 方法將由序列化調用,前提是如果此方法存在,而且它可以通過被序列化對象的類中定義的一個方法訪問。因此,該方法可以擁有私人 (private)、受保護的 (protected) 和包私人 (package-private) 訪問。子類對此方法的訪問遵循 java 訪問規則。

  在從流中讀取類的一個執行個體時需要指定替代的類應使用的準確簽名來實現此特殊方法。

  ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

  此 readResolve 方法遵循與 writeReplace 相同的調用規則和訪問規則。

  序列化運行時使用一個稱為 serialVersionUID 的版本號碼與每個可序列化類別相關聯,該序號在還原序列化過程中用於驗證序列化對象的寄件者和接收者是否為該對象載入了與序列化相容的類。如果接收者載入的該對象的類的 serialVersionUID 與對應的寄件者的類的版本號碼不同,則還原序列化將會導致 InvalidClassException。可序列化類別可以通過聲明名為 "serialVersionUID" 的欄位(該欄位必須是靜態 (static)、最終 (final) 的 long 型欄位)顯式聲明其自己的 serialVersionUID:

  ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

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

  --------------------------

  實現java.io.Serializable 介面的類是可序列化的。沒有實現此介面的類將不能使它們的任一狀態被序列化或逆序列化。

  序列化類別的所有子類本身都是可序列化的。這個序列化介面沒有任何方法和域,僅用於標識序列化的語意。允許非序列化類別的子類型序列化,子類型可以假定負責儲存和恢複父類型的公有的、保護的和(如果可訪問)包的域的狀態。只要該類(擴充)有一個無參構造子,可初始化它的狀態,那麼子類型就可承擔上述職責。在這種情況下申明一個可序列化的類是一個錯誤。此錯誤將在運行時被檢測。就是可以把對象存到位元組流,然後可以恢複!

  例如:Integer實現了Serializable,所以可以把一個Integer的對象用IO寫到檔案裡,之後再可以從檔案裡讀出,如你開始寫入的時候那個對象的intValue() 是5的話,那讀出來之後也是5。這一點體現了用序化類的作用,即用來傳送類的對象。

  當一個JavaBean在構造工具內被使用者化,並與其它Bean建立串連之後,它的所有狀態都應當可被儲存,下一次被load進構造工具內或在運行時,就應當是上一次修改完的資訊。為了能做到這一點,要把Bean的某些欄位的資訊儲存下來,在定義Bean時要使它實現Java.io.Serializable介面。例如:

  public class Button implements Java.io.Serializable {……}

  實現了序列化介面的Bean中欄位的資訊將被自動儲存。若不想儲存某些字(這裡的Bean中欄位的資訊將被自動儲存是什麼意思?這個自動儲存是怎麼實現的?)

  段的資訊則可在這些欄位前冠以transient或static關鍵字,transient和static變數的資訊是不可被儲存的。通常,一個Bean所有公開出來的屬性都應當是被儲存的,也可有選擇地儲存內部狀態。Bean開發人員在修改軟體時,可以添加欄位,移走對其它類的引用,改變一個欄位的private、protected或public狀態,這些都不影響類的儲存結構關係。然而,當從類中刪除一個欄位,改變一個變數在類體系中的位置,把某個欄位改成transient/static,或原來是transient/static,現改為別的特性時,都將引起儲存關係的變化。

  所謂的Serializable,就是java提供的通用資料儲存和讀取的介面。至於從什麼地方讀出來和儲存到哪裡去都被隱藏在函數參數的背後了。這樣子,任何類型只要實現了Serializable介面,就可以被儲存到檔案中,或者作為資料流通過網路發送到別的地方。也可以用管道來傳輸到系統的其他程式中。這樣子極大的簡化了類的設計。只要設計一個儲存一個讀取功能就能解決上面說得所有問題。

serialize是什麼意思?
  這個詞,在中文中翻譯為序列化或者序列化。這兩個翻譯應該說幾乎都無法表達它的實際意義。如果你原本不懂,給你翻譯了,你還是不懂。
  事實上這是IO儲存中的一個概念。電腦中所有的內容都是0或者1的數字這一點,我想現在可能連五歲頑童也是知道的。這些數字按順序排列,便可以表示電腦中的萬事萬物,當然Java的對象也不例外。在記憶體中,無論如何表示,對我們的操作都沒有影響,所以我們不必關心。但是,當我們需要把Object Storage Service到硬碟、資料庫或其它相關介質時,我們就需要考慮這個表示格式或者轉化方法了。
  那麼Java的對象要如何轉化為這0、1的序列呢?就是靠序列化。不過大家不必擔心,Java在這方面很貼心,不需要你們自己去推敲轉化演算法,只需要使用java.io.ObjectOutputStream類即可完成複雜的轉化工作。但ObjectOutputStream類可以支援的只有實現了Serializable介面的對象,如果沒有實現Serializable介面,則會拋出NotSerializableException。

transient
  但是,在一個你希望序列化的類中,常常可能參雜了一些臨時性成員變數或者一些你並不希望它們被儲存下來的屬性,這些成員變數或者屬性,就需要用transient關鍵字修飾。它們會在序列化時被忽略。當從儲存介質中讀取並還原成對象時,他們會被還原為屬性的預設值(基本類型為0或false,對象為null)。
  這就是有關Java中的serialize的一個概述。詳細內容可以參考Sun Java Tutorial中關於IO的一章。

為什麼項目中的所有Java Bean都要實現Serializable介面?
  這一點要從WebSphere Application Server(以下簡稱WAS)的Session共用機制說起。在日本的環境上,WAS伺服器是互為備份的兩台,通過負載平衡dispatcher來分配用戶端發來的請求。Session在Web開發中是一個極其重要的儲存單位,與使用者相關的內容,常常儲存在伺服器端的Session容器中。如果僅有一台WAS伺服器,Session容器僅建立在記憶體中即可,但當有兩台互為備份的WAS伺服器時,它們必須共用Session才可以讓用戶端感覺不出伺服器的切換過程。而Session的共用顯然無法通過記憶體實現,必須使用外部存放裝置。在WAS中,常用的方法是將Session儲存在資料庫中。這時,就需要將Session進行序列化,也勢必會開始序列化所有Session中儲存的內容。為了讓這一操作正常進行,所有可能儲存在Session中的Java Bean都必須支援序列化,也就必須實現Serializable介面。
  由此可見,並不是所有的Java Bean都需要實現Serializable介面,而只是可能放在Session中的Java Bean有這個必要。但當年項目中的經驗型技術高手沒有確切地分析清楚問題的根本原因,所以只能根據經驗想出了一個權宜之計。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.