• 关于serialVersionUID的说明 分类: B1_JAVA 2014-05-24 11:02 1334人阅读 评论(0) 收藏



    1、为什么要使用serialVersionUID

    (1)对于实现了Serializable接口的类,可以将其序列化输出至磁盘文件中,同时会将其serialVersionUID输出到文件中。

    (2)然后有需要使用时,再从磁盘将对象内容及serialVersionUID读入内容中的某个对象。

    (3)将磁盘内容读入对象时,需要进行强制类型转换,如Person person = (Person)ois.readObject(); 

    (4) 此时,将对比从磁盘读入的Serializable与对象所属类(如Person)的Serializable,若二者一致,则转换成功。若二者不一致,则转换失败,并抛出InvalidClassException。

    如果没有为类指定serialVersionUID,则JVM会自动根据类的内容生成一个serialVersionUID,类中的任何变化均会导致serialVersionUID的变化,如新增一个空格。

    因此,若一个类没有指定serialVersionUID,而且发生了变化,则读取磁盘中的对象时就会报错。


    2、何时应该修改serialVersionUID

    若对象已经修改较多或者修改成不兼容的模式,导致原来输出到磁盘的内容不应再转换至原对象,此时则应该修改serialVersionUID。

    When should update your serialVersionUID?
    When your serialization class is updated with some incompatible Java type changes to a serializable class, you have to update your serialVersionUID.


    3、如何创建serialVersionUID

    以下内容参考:http://zengxiankang2011.blog.163.com/blog/static/1783603192011594938588/?fromdm&isFromSearchEngine=yes

    在Eclipse中,提供两种方式让我们快速添加SerialVersionUid。

    add default serial version ID:
    Adds a default serial version ID to the selected type
    Use this option to add a user-defined ID in combination with custom serialization code if the type did undergo structural change since its first release.

    add generated serial version ID:
    Adds a generated serial version ID to the selected type
    Use this option to add a compiler-generated ID if the type didnot undergo structural change since its first release.


    一种就是1L,一种是生成一个很大的数,这两种有什么区别呢?

    看上去,好像每个类的这个类不同,似乎这个SerialVersionUid在类之间有某种关联。其实不然,两种都可以,从JDK文档也看不出这一点。我们只要保证在同一个类中,不同版本根据兼容需要,是否更改SerialVersionUid即可。

    对于第一种,需要了解哪些情况是可兼容的,哪些根本就不兼容。 参考文档:http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf

    在可兼容的前提下,可以保留旧版本号,如果不兼容,或者想让它不兼容,就手工递增版本号。

    1->2->3.....


    第二种方式,是根据类的结构产生的hash值。增减一个属性、方法等,都可能导致这个值产生变化。我想这种方式适用于这样的场景:

    开发者认为每次修改类后就需要生成新的版本号,不想向下兼容,操作就是删除原有serialVesionUid声明语句,再自动生成一下。


    以下内容转自http://blog.csdn.net/jimforme/article/details/5120587

    在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中,说白了,就是能将一个2进制文件变成内存中的对象。在JAVA中,要实现这种机制,只要实现Serializable接口就可以了,先看下面这个简单例子,serialVersionUID稍后引出。我们先定义一个简单的Person类,然后创建这个对象,最后序列化它到一个文件。

    /*****(Person类)*******/
    import java.io.Serializable;   
    public class Person implements Serializable {     
        private String name;     
        public String getName() { 
            return name; 
        } 
        public void setName(String name) { 
            this.name = name; 
        } 
    } 

     

    /*****(将对象序列化到一个文件)*******/

    import java.io.FileOutputStream; 
    import java.io.ObjectInputStream; 
    import java.io.ObjectOutputStream; 
      
    public class WhySerialversionUID {   
    public static void main(String[] args) throws Exception {        
    Person person= new Person(); 
    person.setName("jack");   
    ObjectOutputStream oo = new ObjectOutputStream  (new FileOutputStream(new File("E://jack.test"))); 
                                 oo.writeObject(person); 
    oo.close(); 


     

    /*****(通过以下方法可以正常的将文件中保存的对象还原到内存中)*******/

    import java.io.FileOutputStream; 
    import java.io.ObjectInputStream; 
    import java.io.ObjectOutputStream; 
      
    public class WhySerialversionUID {   
    public static void main(String[] args) throws Exception {   
     ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E://jack.test")));
               Person person = (Person)ois.readObject();    
                String name= person.getName();
              System.Out.Print("name is: "+name);


    一切都那么顺利,但是如果在序列化之后,Person这个类发生了改变呢?比如,多了一个成员变量。我们做如下试验,还是先将对象序列化到一个文件中,之后在Person这个类中添加一个成员变量,如下: 

    import java.io.Serializable;   
    public class Person implements Serializable { 
            private String name; 
        //添加这么一个成员变量 
        private String address;     
        public String getName() { 
            return name; 
        } 
        public void setName(String name) { 
            this.name = name; 
        } 
    }

     

    之后,我们再去运行一下还原,就发现运行出错了,会报如下错误:
    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;在引用serializable这个类的前面有一个感叹号,单击这个感叹号后会有提示,一个是默认的,一个为此类自动产生一个SerialVersionUID!



    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    Spring5基础
    前端离职工作项目交接清单
    【Elastic2】SpringBoot整合ELK、SpringBoot写ES
    【Elastic1】ELK基本概念、环境搭建、快速开始文档
    文件挂载(四) windows挂载linux文件夹
    mysql锁排查
    文件挂载(二) windows挂载windows文件夹
    Netty源码死磕(ChannelPipeline的执行过程)
    使用 NetCoreBeauty 优化 .NET CORE 独立部署目录结构
    WPF学习笔记(四):AvalonEdit 代码高亮编辑控件专题
  • 原文地址:https://www.cnblogs.com/lujinhong2/p/4637325.html
Copyright © 2020-2023  润新知