1. 程式人生 > >java物件實現Serializable介面(整理)

java物件實現Serializable介面(整理)



Serializable 在工作中很少用到 之前也懂一些 今天特意整理一下

在還沒有深入瞭解serializable介面之前,像很多程式設計師一樣,以為一個物件實現serializable介面就被序列化了。 最近在接觸ehcache快取的時候,將物件快取起來,該物件需要先實現Serializable介面,然而,我們會發現物件並沒有真正的被序列化。 下面讓我們一起來總結一下Serializable介面的實現原理。 當一個類實現了Seializable介面(該介面僅為標記介面,不包含任何方法定義),表示該類可以序列化,序列化的目的是將一個實現了Serializable介面的物件可以轉換成一個位元組序列,儲存物件的狀態。
把該位元組序列儲存起來(例如:儲存在一個檔案裡),以後可以隨時將該位元組序列恢復為原來的物件。甚至可以將該位元組序列放到其他計算機上或者通過網路傳輸到其他計算機上恢復,只有該計算機平臺存在相應的類就可以正常恢復為原來的物件。 一個物件實現Serializable介面序列化,先要建立某些OutputStream物件,然後將其封裝在一個ObjectOutputStream物件內,再呼叫writeObject()方法,即可序列化一個物件,反序列化,InputStream,再呼叫readObject()方法。(writeObject和readObject本身就是執行緒安全的,傳輸過程中是不允許被併發訪問的,所以物件只能一個一個接連不斷的傳過來)。
如果某個類能夠被序列化,其子類也可以被序列化。宣告為static和transient型別的成員資料不能被序列化。因為static代表類的狀態,transient代表物件的臨時資料,static物件變數在反序列化時取得的值為當前jvm中對應類中對應static變數的值,而transient(瞬態)關鍵字則一般用於標識那些在序列化時不需要傳遞的狀態變數。 Person類
  1. package org.test.domain;  
  2. import java.io.Serializable;  
  3. publicclass Person implements Serializable{  
  4.     /** 
  5.      *  
  6.      */
  7.     privatestaticfinallong serialVersionUID = 1L;  
  8.     protected String name;  
  9.     protectedtransientint age;  
  10.     public Person(){}  
  11.     public Person(String name,int age){  
  12.         this.name = name;  
  13.         this.age = age;       
  14.     }  
  15.     public String getName() {  
  16.         return name;  
  17.     }  
  18.     publicvoid setName(String name) {  
  19.         this.name = name;  
  20.     }  
  21.     publicint getAge() {  
  22.         return age;  
  23.     }  
  24.     publicvoid setAge(int age) {  
  25.         this.age = age;  
  26.     }  
  27.     public String toString()  
  28.     {  
  29.         return"this is person:"+"name:"+this.name+"——age:"+this.age;  
  30.     }  
  31. }  

User類
  1. package org.test.domain;  
  2. import java.io.Serializable;  
  3. publicclass User extends Person{  
  4.     /** 
  5.      *  
  6.      */
  7.     privatestaticfinallong serialVersionUID = 1L;  
  8.     private String name;  
  9.     private String password;  
  10.     public User() {  
  11.     }  
  12.     public User(String name,String password,int age)  
  13.     {  
  14.         this.name = name;  
  15.         this.password = password;  
  16.         this.age = age;  
  17.     }  
  18.     public String getName() {  
  19.         return name;  
  20.     }  
  21.     publicvoid setName(String name) {  
  22.         this.name = name;  
  23.     }  
  24.     public String getPassword() {  
  25.         return password;  
  26.     }  
  27.     publicvoid setPassword(String password) {  
  28.         this.password = password;  
  29.     }  
  30.     public String toString()  
  31.     {  
  32.         return"this is user:"+"name:"+this.name+"——password:"+this.password+"——age:"+this.age;  
  33.     }  
  34. }  

SerializableTest
  1. package org.test.main;  
  2. import java.io.ByteArrayInputStream;  
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.ObjectInputStream;  
  6. import java.io.ObjectOutputStream;  
  7. import org.test.domain.Person;  
  8. import org.test.domain.User;  
  9. publicclass SerializableTest {  
  10.     publicstaticvoid main(String[] args) {  
  11.         Person p1 = (Person)deSerialByte(serialByte(new User("user","1234",15)));  
  12.         //Person p2 = (Person)deSerialByte(serialByte(new Person("person",10)));
  13.         System.out.println("p1:"+p1.toString());  
  14.         //System.out.println("p2:"+p2.toString());
  15.     }  
  16.     //序列化一個物件(可以儲存到一個檔案也可以儲存到位元組陣列)這裡儲存到自己陣列
  17.     publicstaticbyte[] serialByte(Object obj)  
  18.     {  
  19.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  20.         ObjectOutputStream oos;  
  21.         try {  
  22.             oos = new ObjectOutputStream(baos);  
  23.             oos.writeObject(obj);  
  24.             oos.close();  
  25.             return baos.toByteArray();  
  26.         } catch (IOException e) {  
  27.             thrownew RuntimeException(e.getMessage());  
  28.         }  
  29.     }  
  30.     //反序列化一個物件
  31.     publicstatic Object deSerialByte(byte[] by)  
  32.     {  
  33.         ObjectInputStream ois;  
  34.         try {  
  35.             ois = new ObjectInputStream(new ByteArrayInputStream(by));  
  36.             return ois.readObject();  
  37.         } catch (Exception e) {  
  38.             thrownew RuntimeException(e.getMessage());  
  39.         }  
  40.     }  
  41. }  

serialVersionUID作用: 
       序列化時為了保持版本的相容性,即在版本升級時反序列化仍保持物件的唯一性。
有兩種生成方式:
       一個是預設的1L,比如:private static final long serialVersionUID = 1L;
       一個是根據類名、介面名、成員方法及屬性等來生成一個64位的雜湊欄位,比如:
       private static final   long     serialVersionUID = xxxxL;

當你一個類實現了Serializable介面,如果沒有定義serialVersionUID,Eclipse會提供這個提示功能告訴你去定義 。在Eclipse中點選類中warning的圖示一下,Eclipse就會自動給定兩種生成的方式。如果不想定義它,在Eclipse的設定中也
       可以把它關掉的,設定如下: 
        Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==>
        Potential programming problems 
        將Serializable class without serialVersionUID的warning改成ignore即可。

如果你沒有考慮到相容性問題時,就把它關掉,不過有這個功能是好的,只要任何類別實現了Serializable這個介面的話,如果沒有加入serialVersionUID,Eclipse都會給你warning提示,這個serialVersionUID為了讓該類別Serializable向後相容。

如果你的類Serialized存到硬碟上面後,可是後來你卻更改了類別的field(增加或減少或改名),當你Deserialize時,就會出現Exception的,這樣就會造成不相容性的問題。

但當serialVersionUID相同時,它就會將不一樣的field以type的預設值Deserialize,可避開不相容性問題。

注意以下幾點:

1、若繼承的父類沒有實現Serializable介面,但是又想讓子類可序列化,子類實現Serializable介面,子類必須有可訪問的無參構造方法,用於儲存和恢復父類的public或protected或同包下的package欄位的狀態,否則在序列化或反序列化時會丟擲RuntimeException異常,對於序列化後的子類,在進行反序列化時,理論上無法初始化父類中private(不可訪問)物件變數的狀態或值。

2、在對可序列化類中的屬性進行序列化時,如果遇到不可序列化的物件變數,此時會針對不可序列化的類丟擲NotSerializableException異常

3、對於可序列化的非陣列類,強烈建議顯示宣告static型、long型、final型serialVersionUID欄位用於標識當前序列化類的版本號,否則在跨作業系統、跨編譯器之間進行序列化和反序列化時容易出現InvalidClassException異常