java序列化機制學習

來源:互聯網
上載者:User

什麼是序列化
java中的序列化(serialization)機制能夠將一個執行個體對象的狀態資訊寫入到一個位元組流中,使其可以通過socket進行傳輸、或者持久化儲存到資料庫或檔案系統中;然後在需要的時候,可以根據位元組流中的資訊來重構一個相同的對象。序列化機制在java中有著廣泛的應用,EJB、 RMI等技術都是以此為基礎的。

正確使用序列化機制
一般而言,要使得一個類可以序列化,只需簡單實現java.io.Serializable介面即可。該介面是一個標記式介面,它本身不包含任何內容,實現了該介面則表示這個類準備支援序列化的功能。如下例定義了類Person,並聲明其可以序列化。

Java代碼

   1. public class Person implements java.io.Serializable {}  

public class Person implements java.io.Serializable {}

序列化機制是通過java.io.ObjectOutputStream類和java.io.ObjectInputStream類來實現的。在序列化(serialize)一個對象的時候,會先執行個體化一個ObjectOutputStream對象,然後調用其 writeObject()方法;在還原序列化(deserialize)的時候,則會執行個體化一個ObjectInputStream對象,然後調用其 readObject()方法。下例說明了這一過程。
Java代碼

   1. public void serializeObject(){  
   2.      String fileName = "ser.out";  
   3.      FileOutputStream fos = new FileOutputStream(fileName);  
   4.      ObjectOutputStream oos = new ObjectOutputStream(fos);  
   5.      oos.writeObject(new Person());  
   6.      oos.flush();  
   7. }  
   8.   
   9. public void deserializeObject(){  
  10.      String fileName = "ser.out";  
  11.      FileInputStream fos = new FileInputStream(fileName);  
  12.      ObjectInputStream oos = new ObjectInputStream(fos);  
  13.      Person p = oos.readObject();  
  14. }  

上例中我們對一個Person對象定義了序列化和還原序列化的操作。但如果Person類是不能序列化的話,即對不能序列化的類進行序列化操作,則會拋出 java.io.NotSerializableException異常。
JVM中有一個預定義的序列化實現機制,即預設調用 ObjectOutputStream.defaultWriteObject() 和 ObjectInputStream.defaultReadObject() 來執行序列化操作。如果想自訂序列化的實現,則必須在聲明了可序列化的類中實現 writeObject()和readObject()方法。

幾種使用方式
一般在序列化一個類A的時候,有以下三種情況:
# [list=3] 類A沒有父類,自己實現了Serializable介面
# 類A有父類B,且父類實現了Serializable介面
# 類A有父類B,但父類沒有實現Serializable介面
[/list]
對於第一種情況,直接實現Serializable介面即可。
對於第二種情況,因為父類B已經實現了Serializable介面,故類A無需實現此介面;如果父類實現了writeObject()和readObject(),則使用此方法,否則直接使用預設的機制。
對於第三種情況,則必須在類A中顯示實現writeObject()和readObject()方法來處理父類B的狀態資訊;還有一點要特別注意,在父類B中一定要有一個無參的建構函式,這是因為在還原序列化的過程中並不會使用聲明為可序列化的類A的任何建構函式,而是會調用其沒有申明為可序列化的父類B的無參建構函式。

序列化機制的一些問題
# [list] 效能問題
為了序列化類別A一個執行個體對象,所需儲存的全部資訊如下:
1. 與此執行個體對象相關的全部類的中繼資料(metadata)資訊;因為繼承關係,類A的執行個體對象也是其任一父類的對象。因而,需要將整個繼承鏈上的每一個類的中繼資料資訊,按照從父到子的順序依次儲存起來。
2. 類A的描述資訊。此描述資訊中可能包含有如下這些資訊:類的版本ID(version ID)、表示是否自訂了序列化實現機制的標誌、可序列化的屬性的數目、每個屬性的名字和值、及其可序列化的父類的描述資訊。
3. 將執行個體對象作為其每一個超類的執行個體對象,並將這些資料資訊都儲存起來。
# 在RMI等遠程調用的應用中,每調用一個方法,都需要傳遞如此多的資訊量;久而久之,會對系統的效能照成很大的影響。 版本資訊
當用readObject()方法讀取一個序列化對象的byte流資訊時,會從中得到所有相關類的描述資訊以及樣本對象的狀態資料;然後將此描述資訊與其本地要構造的類的描述資訊進行比較,如果相同則會建立一個新的執行個體並恢複其狀態,否則會拋出異常。這就是序列化對象的版本檢測。JVM中預設的描述資訊是使用一個長整型的雜湊碼(hashcode)值來表示,這個值與類的各個方面的資訊有關,如類名、類修飾符、所實現的介面名、方法和建構函式的資訊、屬性的資訊等。因而,一個類作一些微小的變動都有可能導致不同的雜湊碼值。例如開始對一個執行個體對象進行了序列化,接著對類增加了一個方法,或者更改了某個屬性的名稱,當再想根據序列化資訊來重構以前那個對象的時候,此時兩個類的版本資訊已經不匹配,不可能再恢複此對象的狀態了。要解決這個問題,可能在類中顯示定義一個值,如下所示:
Java代碼

   1. private static final long serialVersionUID = ALongValue;  

這樣,序列化機制會使用這個值來作為類的版本標識符,從而可以解決不相容的問題。但是它卻引入了一個新的問題,即使一個類作了實質性的改變,如增加或刪除了一些可序列化的屬性,在這種機制下仍然會認為這兩個類是相等的。
[/list]
一種更好的選擇
作為實現Serializable介面的一種替代方案,實現java.io.Externalizable介面同樣可以標識一個類為可序列化。
Externalizable介面中定義了以下兩個方法:
Java代碼

   1. public void readExternal(ObjectInput in);  
   2. public void writeExternal(ObjectOutput out); 

這兩個方法的功能與 readObject()和writeObject()方法相同,任何實現了Externalizable介面的類都需要這實現兩個函數來定義其序列化機制。
使用Externalizable比使用Serializable有著效能上的提高。前者序列化一個對象,所需儲存的資訊比後者要小,對於後者所需儲存的第3個方面的資訊,前者不需要訪問每一個父類並使其儲存相關的狀態資訊,而只需簡單地調用類中實現的writeExternal()方法即可。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.