java對象序列化

  下面的內容是轉載的,感覺寫的非常好,真是學習了。 
 當我們需要序列化一個JAVA對象時需要實現Serializable接口。這個接口僅僅是一個tag接口,並不需要你真正實現一些方法,因爲這個接口沒有方法。他作用僅僅是告訴默認JAVA序列化工具,這個對象是可以序列化的。

1.serialVersionUID的作用
    當我們的類實現了Serializable接口後,會有一個警告,告訴你需要生成一個serialVersionUID屬性。這個serialVersionUID是做什麼用的呢?其實這是JAVA序列化的版本控制功能。當序列化對象時會把這個屬性寫入,當反序列化時則會把這個屬性取出,然後與JAVA類中的serialVersionUID屬性值對比,如果一致,則認爲是同一個版本,正常反序列化,如果不一致則認爲版本不同,拋出InvalidClassException異常。

    很多時候我們忽略這個警告,並不寫這個serialVersionUID屬性,但仍然可以正常序列化。那是因爲如果沒有這個屬性,JVM將會根據這個類的屬性和方法,計算出一個值作爲serialVersionUID的值。這種做法會帶來潛在的風險。不同的JVM產生serialVersionUID的算法可能會不一致,如果在不同的環境下產生的serialVersionUID不一致,將導致反序列化失敗!

    當一個類的結構發生變化,需要改變serialVersionUID,以通知序列化機制此類發生了變化,不兼容原來的版本了!其實並不是只要類結構變化,就必須更改serialVersionUID,JAVA的序列化機制提供了部分變化的兼容機制,有如下幾種:
• 添加新的屬性 反序列時發現沒有此屬性,則會賦予該屬性默認的類型值
• 添加 writeObject/readObject 方法 因爲此方法是用於自定義序列化,不影響序列化
• 刪除 writeObject/readObject 方法 同上原因
• 改變屬性的訪問權限(private protected package public)
• 將一個屬性從static變爲nonstatic 或者 transient 或者 nontransient

如果你的類改動屬於以上範圍,默認的JAVA序列化機制可以保證兼容性,也就是說你不需要改動serialVersionUID值。
更多情況參見http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf 5.6.2 Compatible Changes

    其實大部分情況下我們不需要深究哪些改動會影響兼容性。如果我們的序列化僅僅是用作緩存的話,我們可以簡單處理,只要改動了類結構,即修改serialVersionUID值,拋棄原先的序列化結果,重新生成!

JAVA序列化過程
    JAVA默認的序列化類是ObjectOutputStream,反序列化類是ObjectInputStream。
Java代碼 
public static void main(String[] args) throws Exception {  
   
     Object o=new Object();  
       
         try {  
            //序列化  
           FileOutputStream ostream = new FileOutputStream("t.txt");  
             ObjectOutputStream p = new ObjectOutputStream(ostream);  
             p.writeObject(o); // 序列化對象,在內部通過調用defaultWriteFields(Object obj, ObjectStreamClass desc)來序列化對象的所有屬性。  
            p.flush();  
             ostream.close();  
             //反序列化  
            FileInputStream fis=new FileInputStream("t.txt");  
             ObjectInputStream istream=new ObjectInputStream(fis);  
            Object s=(ObjectSerializable) istream.readObject();//反序列化對象,內部調用defaultReadFields(Object obj, ObjectStreamClass desc)來反序列化所有屬性  
             System.out.println(s);  
             istream.close();  
             fis.close();  
         } catch (IOException ioe) {  
             ioe.printStackTrace();  
         } 



JAVA自定義序列化
自定義序列化根據定製程度的不同,有多種定製方案。
1.Externalizable定製
Externalizable接口繼承了Serializable,其中有2個方法:
Java代碼 
void writeExternal(ObjectOutput out) throws IOException; 
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; 

通過實現Externalizable這個接口,我們可以定製需要序列化的屬性。
Java代碼 
/* 
* ObjectOutputStream在調用writeObject()方法時,會判斷需要序列化的類是否繼承了 
*Externalizable接口,如果是,則會調用writeExternal(ObjectOutput out)執行我們 
*自己寫的序列化代碼 
*/  
p.writeObject(o);  

Java代碼 
/* 
*同理,ObjectInputStream在調用readObject()方法時,會判斷需要反序列化的類是否繼承 
*了Externalizable接口,如果是,則會調用readExternal(ObjectInput in)執行我們自己
*寫的反序列化代碼
*/ 
Object s=(ObjectSerializable) istream.readObject() 

2.重寫ObjectOutputStream/ObjectInputStream
實現Externalizable的方式自定義序列化非常方便,只需要在序列化類內部添加2個方法即可,不需要外部的任何要求。
但是如果我們需要更加深度的定製這還是不夠的。Externalizable無法定製序列化對象本身的描述,只能定製對象內部屬性的描述。
此時我們需要新建一個自己的序列化類來實現。
Java代碼 
/** 
* 自定義序列化類 
*/  
public class CustomObjectOutputStream extends ObjectOutputStream {  
  
     private OutputStream cusOut;  
       
    public CustomObjectOutputStream(OutputStream out) throws IOException{  
       /* 
         * 通過調用super()可以將父類的enableOverride設置爲true 
         * 當調用父類的writeObject(obj)時,因爲enableOverride=true,會調用writeObjectOverride(Object obj)方法 
         * 因此我們需要覆寫writeObjectOverride(Object obj)如下所示 
         */  
       super();  
        cusOut=out;  
    }  
      
     @Override  
    protected void writeObjectOverride(Object obj) throws IOException {  
        //自定義序列化方案  
         //cusOut.write(b)...  
   }  
 


Java代碼 
/** 
* 自定義反序列化類 
*/  
public class CustomObjectInputStream extends ObjectInputStream{  
       
     private InputStream cusIn;  
  
     public CustomObjectInputStream(InputStream in)throws IOException {  
        /* 
          * 通過調用super()可以將父類的enableOverride設置爲true 
          * 當調用父類的readObject()時,因爲enableOverride=true,會調用readObjectOverride()方法 
         * 因此我們需要覆寫readObjectOverride()如下所示 
          */  
         super();  
         cusIn=in;  
     }  
       
     @Override  
     protected Object readObjectOverride() throws IOException,  
            ClassNotFoundException {  
        //自定義反序列化方案  
         //cusIn.read() ...  
         return null;  
    }  
 


以上2段代碼繼承了ObjectOutputStream和ObjectInputStream,完全自定義了序列化方法。
既然是完全自定義序列化方法,其實完全沒有必要去繼承ObjectOutputStream和ObjectInputStream。
上面的代碼僅僅適用於做序列化的適配器。其他序列化機制比如hessian,protobuf等,需要適配JAVA默認的序列化機制,則可採用以上的方法適配
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章