學習SSH demo過程的筆記

對象序列化爲何要定義serialVersionUID的來龍去脈

在很多應用中,需要對某些對象進行序列化,讓它們離開內存空間,入住物理硬盤,以便長期保存。比如最常見的是Web服務器中的Session對象,當有10萬用戶併發訪問,就有可能出現10萬個Session對象,內存可能吃不消,於是Web容器就會把一些seesion先序列化到內存,等要用了,再還原到對象中,說白了,就是能將一個2進制文件變成內存中的對象。在JAVA中,要實現這種機制,只要實現Serializable接口就可以了,先看下面這個簡單例子,serialVersionUID稍後引出。我們先定義一個簡單的Person類,然後創建這個對象,最後序列化它到一個文件。 

Java代碼  收藏代碼
  1. import java.io.Serializable;  
  2.    
  3. public class Person implements Serializable {  
  4.      
  5.     private String name;  
  6.      
  7.     public String getName() {  
  8.         return name;  
  9.     }  
  10.     public void setName(String name) {  
  11.         this.name = name;  
  12.     }  
  13. }  
  14. import java.io.FileInputStream;  
  15. import java.io.FileOutputStream;  
  16. import java.io.ObjectInputStream;  
  17. import java.io.ObjectOutputStream;  
  18.    
  19. public class WhySerialversionUID {  
  20.    
  21. public static void main(String[] args) throws Exception {  
  22.    
  23. //這裏是把對象序列化到文件         
  24. Person crab = new Person();  
  25. crab.setName("Mr.Crab");  
  26.    
  27. ObjectOutputStream oo = new ObjectOutputStream  
  28.     (new FileOutputStream("crab_file"));  
  29. oo.writeObject(crab);  
  30. oo.close();  
  31.    
  32. //這裏是把對象序列化到文件,我們先註釋掉,一會兒用  
  33. //ObjectInputStream oi = new ObjectInputStream  
  34. //    (new FileInputStream("crab_file"));  
  35. //Person crab_back = (Person) oi.readObject();  
  36. //System.out.println("Hi, My name is " + crab_back.getName());  
  37. //oi.close();  
  38.    
  39.     }  
  40. }  

運行完後,我們發現有了一個crab_file文件,這個文件就保存這crab對象在內存中的形態。同樣,我們把這部分代碼註釋掉,運行下面那段還原代碼,發現,crab_file文件可以被轉化爲一個對象。 

一切都那麼順利,但是如果在序列化之後,Person這個類發生了改變呢?比如,多了一個成員變量。我們做如下試驗,還是先將對象序列化到一個文件中,之後在Person這個類中添加一個成員變量,如下: 
Java代碼  收藏代碼
  1. import java.io.Serializable;  
  2.    
  3. public class Person implements Serializable {  
  4.      
  5.     private String name;  
  6.     //添加這麼一個成員變量  
  7.     private String address;  
  8.      
  9.     public String getName() {  
  10.         return name;  
  11.     }  
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15. }  

之後,我們再去運行一下還原,就發現運行出錯了,會報如下錯誤: 
Exception in thread “main” java.io.InvalidClassException: Person; local class incompatible: stream classdesc serialVersionUID = 8383901821872620925, local class serialVersionUID = -763618247875550322 
意思就是說,文件流中的class和classpath中的class,也就是修改過後的class,不兼容了,處於安全機制考慮,程序拋出了錯誤,並且拒絕載入。那麼如果我們真的有需求要在序列化後添加一個字段或者方法呢?應該怎麼辦?那就是自己去指定serialVersionUID。之前,在我們的例子中,我們是沒有指定serialVersionUID的,那麼java編譯器會自動給這個class進行一個摘要算法,類似於指紋算法,只要這個文件多一個空格,得到的UID就會截然不同的,可以保證在這麼多類中,這個編號是唯一的。所以,我們添加了一個字段後,由於沒有顯指定serialVersionUID,編譯器又爲我們生成了一個UID,當然和前面保存在文件中的那個不會一樣了,於是就出現了2個號碼不一致的錯誤。因此,只要我們自己指定了serialVersionUID,就可以在序列化後,去添加一個字段,或者方法,而不會影響到後期的還原,還原後的對象照樣可以使用,而且還多了方法可以用,呵呵。但是serialVersionUID我們怎麼去生成呢?你可以寫1,也可以寫2,都無所謂,但是最好還是按照摘要算法,生成一個惟一的指紋數字,eclipse可以自動生成的,jdk也自帶了這個工具。一般寫法類似於 
private static final long serialVersionUID = -763618247875550322L;


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章