对象序列化为何要定义serialVersionUID的来龙去脉

来源:互联网 发布:玄彬韩国地位知乎 编辑:程序博客网 时间:2024/06/11 15:06

转载自 :http://lenjey.iteye.com/blog/513736


在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是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;
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 月子里挤奶手痛怎么办 做完月子之后腿疼腰疼怎么办 腰疼引起的腿疼怎么办 上网上久了脑袋痛怎么办 莲花坐的脚麻怎么办 月子腿疼膝盖疼怎么办 做月子腿着凉了怎么办 出月子大腿根酸怎么办 出了月子腰酸痛怎么办 出了月子腿没劲怎么办 生完孩子后缺钙怎么办 生完孩子腿疼怎么办 生完孩子后腿疼怎么办 生完孩子肛门突出怎么办 生完孩子肋骨突出怎么办 蛙跳理蛙跳后腿疼怎么办 蛙跳两天后腿还疼怎么办 莲花菩提盘黑了怎么办 体育课蛙跳后肌肉拉伤怎么办 o型腿骨头弯了怎么办 小孩钢琴坐姿不对向后仰怎么办 小孩皮肤不好容易留疤怎么办 学游泳时站不稳怎么办 水呛到了不停打嗝怎么办 来月经前游泳了怎么办 快来完事游泳了怎么办 游泳时来月经了怎么办 经期第7天游泳了怎么办 来月经已经游了泳怎么办 月经来了要游泳怎么办 三个月宝宝趴着不会抬头怎么办 我的月经不完怎么办 游泳时怎么办能浮出水面 游泳时眼镜起雾怎么办 练瑜伽手臂变粗怎么办 孕妇喝了芬达怎么办 宫口开了但头高怎么办 整天坐着肚子越来越大怎么办 坐久了屁股变大怎么办 屁股久坐的黑印怎么办 练瑜伽小腿变粗怎么办