1. 程式人生 > >java中為什麼要實現序列化,什麼時候實現序列化?

java中為什麼要實現序列化,什麼時候實現序列化?

序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化,將資料分解成位元組流,以便儲存在檔案中或在網路上傳輸。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決在對物件流進行讀寫操作時所引發的問題。 序列化的實現:將需要被序列化的類實現Serializable介面,該介面沒有需要實現的方法,implements Serializable只是為了標註該物件是可被序列化的,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(物件流)物件,接著,使用ObjectOutputStream物件的writeObject(Object obj)方法就可以將引數為obj的物件寫出(即儲存其狀態),要恢復的話則用輸入流;

序列化分為兩大部分:序列化和反序列化。序列化是這個過程的第一部分,將資料分解成位元組流,以便儲存在檔案中或在網路上傳輸。反序列化就是開啟位元組流並重構物件。物件序列化不僅要將基本資料型別轉換成位元組表示,有時還要恢復資料。恢復資料要求有恢復資料的物件例項 
  序列化的什麼特點: 
  如果某個類能夠被序列化,其子類也可以被序列化。宣告為static和transient型別的成員資料不能被序列化。因為static代表類的狀態, transient代表物件的臨時資料。 
  什麼時候使用序列化: 
  一:物件序列化可以實現分散式物件。主要應用例如:RMI要利用物件序列化執行遠端主機上的服務,就像在本地機上執行物件時一樣。 
  二:java物件序列化不僅保留一個物件的資料,而且遞迴儲存物件引用的每個物件的資料。可以將整個物件層次寫入位元組流中,可以儲存在檔案中或在網路連線上傳遞。利用物件序列化可以進行物件的"深複製",即複製物件本身及引用的物件本身。序列化一個物件可能得到整個物件序列。 
  ====================== 
  可以看看介面java.io.serializable的中文解釋: 
  Serializable 
  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 支援的用於基本資料型別的方法將各個欄位寫入 ObjectOutputStream 來儲存的。 
  readObject 方法負責從流中讀取並還原類欄位。它可以呼叫 in.defaultReadObject 來呼叫預設機制,以還原物件的非靜態和非瞬態欄位。defaultReadObject 方法使用流中的資訊來分配流中通過當前物件中相應命名欄位儲存的物件的欄位。這用於處理類發展後需要新增新欄位的情形。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基本資料型別的方法將各個欄位寫入 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引發的問題——什麼是序列化?在什麼情況下將類序列化? 
  序列化就是一種用來處理物件流的機制
,所謂物件流也就是將物件的內容進行流化。可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間。序列化是為了解決在對物件流進行讀寫操作時所引發的問題。序列化的實現:將需要被序列化的類實現Serializable介面,該介面沒有需要實現的方法,implements Serializable只是為了標註該物件是可被序列化的,然後使用一個輸出流(如:FileOutputStream)來構造一個ObjectOutputStream(物件流)物件,接著,使用ObjectOutputStream物件的writeObject(Object obj)方法就可以將引數為obj的物件寫出(即儲存其狀態),要恢復的話則用輸入流。 
  序列化:序列化是將物件轉換為容易傳輸的格式的過程。例如,可以序列化一個物件,然後使用 HTTP 通過 Internet 在客戶端和伺服器之間傳輸該物件。在另一端,反序列化將從該流重新構造物件。 
  是物件永久化的一種機制。 
  確切的說應該是物件的序列化,一般程式在執行時,產生物件,這些物件隨著程式的停止執行而消失,但如果我們想把某些物件(因為是物件,所以有各自不同的特性)儲存下來,在程式終止執行後,這些物件仍然存在,可以在程式再次執行時讀取這些物件的值,或者在其他程式中利用這些儲存下來的物件。這種情況下就要用到物件的序列化。 
  只有序列化的物件才可以儲存在儲存裝置上。為了物件的序列化而需要繼承的介面也只是一個象徵性的介面而已,也就是說繼承這個介面說明這個物件可以被序列化了,沒有其他的目的。之所以需要物件序列化,是因為有時候物件需要在網路上傳輸,傳輸的時候需要這種序列化處理,從伺服器硬碟上把序列化的物件取出,然後通過網路傳到客戶端,再由客戶端把序列化的物件讀入記憶體,執行相應的處理。 
  物件序列化是java的一個特徵,通過該特徵可以將物件寫作一組位元組碼,當在其他位置讀到這些位元組碼時,可以依此建立一個新的物件,而且新物件的狀態與原物件完全相同。為了實現物件序列化,要求必須能夠訪問類的私有變數,從而保證物件狀態能夠正確的得以儲存和恢復。相應的,物件序列化API能夠在物件重建時,將這些值還原給私有的資料成員。這是對java語言訪問許可權的挑戰。通常用在伺服器客戶端的物件交換上面,另外就是在本機的儲存。 
  物件序列化的最主要的用處就是在傳遞,和儲存物件(object)的時候,保證物件的完整性和可傳遞性。譬如通過網路傳輸,或者把一個物件儲存成一個檔案的時候,要實現序列化介面 。 
  * 
  Quote: 
  比較java.io.Externalizable和java.io.Serializable 
  http://www.zdnet.com.cn/developer/code/story/0,3800066897,39304080,00.htm 
  即使你沒有用過物件序列化(serialization),你可能也知道它。但你是否知道 Java 還支援另外一種形式的物件持久化,外部化(externalization)? 
  下面是序列化和外部化在程式碼級的關聯方式: 
  public interface Serializable {} 
  public interface Externalizable extends Serializable { 
  void readExternal(ObjectInput in); 
  void writeExternal(ObjectOutput out); 
  } 
  序列化和外部化的主要區別 
  外部化和序列化是實現同一目標的兩種不同方法。下面讓我們分析一下序列化和外部化之間的主要區別。 
  通過Serializable介面對物件序列化的支援是內建於核心 API 的,但是java.io.Externalizable的所有實現者必須提供讀取和寫出的實現。Java 已經具有了對序列化的內建支援,也就是說只要製作自己的類java.io.Serializable,Java 就會試圖儲存和重組你的物件。如果使用外部化,你就可以選擇完全由自己完成讀取和寫出的工作,Java 對外部化所提供的唯一支援是介面: 
  voidreadExternal(ObjectInput in) 
  void writeExternal(ObjectOutput out) 
  現在如何實現readExternal() 和writeExternal() 就完全看你自己了。 
  序列化會自動儲存必要的資訊,用以反序列化被儲存的例項,而外部化則只儲存被儲存的類的標識。當你通過java.io.Serializable介面序列化一個物件時,有關類的資訊,比如它的屬性和這些屬性的型別,都與例項資料一起被儲存起來。在選擇走Externalizable這條路時,Java 只儲存有關每個被儲存型別的非常少的資訊。 
  每個介面的優點和缺點 
  Serializable介面 
  · 優點:內建支援 
  · 優點:易於實現 
  · 缺點:佔用空間過大 
  · 缺點:由於額外的開銷導致速度變比較慢 
  Externalizable介面 
  · 優點:開銷較少(程式設計師決定儲存什麼) 
  · 優點:可能的速度提升 
  · 缺點:虛擬機器不提供任何幫助,也就是說所有的工作都落到了開發人員的肩上。 
  在兩者之間如何選擇要根據應用程式的需求來定。Serializable通常是最簡單的解決方案,但是它可能會導致出現不可接受的效能問題或空間問題;在出現這些問題的情況下,Externalizable可能是一條可行之路。 
  要記住一點,如果一個類是可外部化的(Externalizable),那麼Externalizable方法將被用於序列化類的例項,即使這個型別提供了Serializable方法: 
  private void writeObject() 
  private void readObject()