序列化---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 字段作爲繼承成員沒有用處。

==============================================================================

初探序列化---Serializable

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

  Java的"對象序列化"能讓你將一個實現了Serializable接口的對象轉換成一組byte,這樣日後要用這個對象時候,你就能把這些byte數據恢復出來,並據此重新構建那個對象了。

  要想序列化對象,你必須先創建一個OutputStream,然後把它嵌進ObjectOutputStream。這時,你就能用writeObject( )方法把對象寫入OutputStream了。

  writeObject 方法負責寫入特定類的對象的狀態,以便相應的 readObject 方法可以還原它。通過調用 out.defaultWriteObject 可以調用保存 Object 的字段的默認機制。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支持的用於基本數據類型的方法將各個字段寫入 ObjectOutputStream 來保存的。

  讀的時候,你得把InputStream嵌到ObjectInputStream裏面,然後再調用readObject( )方法。不過這樣讀出來的,只是一個Object的reference,因此在用之前,還得先下傳。readObject 方法負責從流中讀取並還原類字段。它可以調用 in.defaultReadObject 來調用默認機制,以還原對象的非靜態和非瞬態字段。

   defaultReadObject 方法使用流中的信息來分配流中通過當前對象中相應命名字段保存的對象的字段。這用於處理類發展後需要添加新字段的情形。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支持的用於基本數據類型的方法將各個字段寫入 ObjectOutputStream 來保存的。

  看一個列子:
 import  java.io. * ;

 class  tree  implements  java.io.Serializable  {
     public  tree left;
     public  tree right;
     public   int  id;
     public   int  level;

     private   static   int  count  =   0 ;

     public  tree( int  depth)  {
        id  =  count ++ ;
        level  =  depth;
         if  (depth  >   0 )  {
            left  =   new  tree(depth - 1 );
            right  =   new  tree(depth - 1 );
        } 
    } 

     public   void  print( int  levels)  {
         for  ( int  i  =   0 ; i  <  level; i ++ )
            System.out.print( "    " );
        System.out.println( " node  "   +  id);

         if  (level  <=  levels  &&  left  !=   null )
            left.print(levels);

         if  (level  <=  levels  &&  right  !=   null )
            right.print(levels);
    } 


     public   static   void  main (String argv[])  {

         try   {
             /*  創建一個文件寫入序列化樹。  */ 
            FileOutputStream ostream  =   new  FileOutputStream( " tree.tmp " );
             /*  創建輸出流  */ 
            ObjectOutputStream p  =   new  ObjectOutputStream(ostream);

             /*  創建一個二層的樹。  */ 
            tree base  =   new  tree( 2 );

            p.writeObject(base);  //  將樹寫入流中。 
             p.writeObject( " LiLy is 惠止南國 " );
            p.flush();
            ostream.close();     //  關閉文件。 
 
              /*  打開文件並設置成從中讀取對象。  */ 
            FileInputStream istream  =   new  FileInputStream( " tree.tmp " );
            ObjectInputStream q  =   new  ObjectInputStream(istream);

             /*  讀取樹對象,以及所有子樹  */ 
            tree new_tree  =  (tree)q.readObject();

            new_tree.print( 2 );   //  打印出樹形結構的最上面 2級 
             String name  =  (String)q.readObject();
            System.out.println( " /n " + name);
        }   catch  (Exception ex)  {
            ex.printStackTrace();
        } 
    } 
}  

 

  最後結果如下:

    node 0
  node 1
node 2
node 3
  node 4
node 5
node 6

LiLy is 惠止南國

  可以看到,在序列化的時候,writeObject與readObject之間的先後順序。readObject將最先write的object read出來。用數據結構的術語來講就姑且稱之爲先進先出吧!

  在序列化時,有幾點要注意的:
  1:當一個對象被序列化時,只保存對象的非靜態成員變量,不能保存任何的成員方法和靜態的成員變量。
  2:如果一個對象的成員變量是一個對象,那麼這個對象的數據成員也會被保存。
  3:如果一個可序列化的對象包含對某個不可序列化的對象的引用,那麼整個序列化操作將會失敗,並且會拋出一個NotSerializableException。我們可以將這個引用標記爲transient,那麼對象仍然可以序列化

  還有我們對某個對象進行序列化時候,往往對整個對象全部序列化了,比如說類裏有些數據比較敏感,不希望序列化,一個方法可以用transient來標識,另一個方法我們可以在類裏重寫
 private   void  readObject(java.io.ObjectInputStream stream)
      throws  IOException, ClassNotFoundException;
  private   void  writeObject(java.io.ObjectOutputStream stream)
      throws  IOException
 

  這二個方法!
  示例:

 import  java.io. * ;

 class  ObjectSerialTest
 {
     public   static   void  main(String[] args)  throws  Exception
     {
        Employee e1 = new  Employee( " zhangsan " , 25 , 3000.50 );
        Employee e2 = new  Employee( " lisi " , 24 , 3200.40 );
        Employee e3 = new  Employee( " wangwu " , 27 , 3800.55 );
       
        FileOutputStream fos = new  FileOutputStream( " employee.txt " );
        ObjectOutputStream oos = new  ObjectOutputStream(fos);
        oos.writeObject(e1);
        oos.writeObject(e2);
        oos.writeObject(e3);
        oos.close();
       
        FileInputStream fis = new  FileInputStream( " employee.txt " );
        ObjectInputStream ois = new  ObjectInputStream(fis);
        Employee e;
         for ( int  i = 0 ;i < 3 ;i ++ )
         {
            e = (Employee)ois.readObject();
            System.out.println(e.name + " : " + e.age + " : " + e.salary);
        }
        ois.close();
    }
}
 
 class  Employee  implements  Serializable
 {
    String name;
     int  age;
     double  salary;
     transient  Thread t = new  Thread();
     public  Employee(String name, int  age, double  salary)
     {
         this .name = name;
         this .age = age;
         this .salary = salary;
    }
     private   void  writeObject(java.io.ObjectOutputStream oos)  throws  IOException
     {
        oos.writeInt(age);
        oos.writeUTF(name);
        System.out.println( " Write Object " );
    }
     private   void  readObject(java.io.ObjectInputStream ois)  throws  IOException
     {
        age = ois.readInt();
        name = ois.readUTF();
        System.out.println( " Read Object " );
    }
 
}

 

轉自:http://blog.163.com/flm_llx/blog/static/2712071220083169341435/

發佈了32 篇原創文章 · 獲贊 0 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章