51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 1740|回复: 1
打印 上一主题 下一主题

Serializable兼容性问题及serialVersionUID的使用

[复制链接]
  • TA的每日心情
    擦汗
    13 小时前
  • 签到天数: 526 天

    连续签到: 3 天

    [LV.9]测试副司令

    跳转到指定楼层
    1#
    发表于 2018-12-28 15:01:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    兼容性问题
    兼容性历来是复杂而麻烦的问题。


    不要兼容性:

          首先来看看如果我们的目的是不要兼容性,应该注意哪些。不要兼容性的场合很多,比如war3每当版本升级就不能够读取以前的replays。

          兼容也就是版本控制,java通过一个名为UID(stream unique identifier)来控制,这个UID是隐式的,它通过类名,方法名等诸多因素经过计算而得,理论上是一一映射的关系,也就是唯一的。如果UID不一 样的话,就无法实现反序列化了,并且将会得到InvalidClassException。

          当我们要人为的产生一个新的版本(实现并没有改动),而抛弃以前的版本的话,可以通过显式的声名UID来实现:

          private static final long serialVersionUID=1l;

    你可以编造一个版本号,但注意不要重复。这样在反序列化的时候老版本将得到InvalidClassException,我们可以在老版本的地方捕捉这个异常,并提示用户升级的新的版本。

    当改动不大时,保持兼容性(向下兼容性的一个特例):

          有时候你的类增加了一些无关紧要的非私有方法,而逻辑字段并不改变的时候,你当然希望老版本和新版本保持兼容性,方法同样是通过显式的声名UID来实现。下面我们验证一下。


    •       老版本:

    import java.io.*;

    public class Serial implements Serializable {

          int company_id;

          String company_addr;

             public Serial1(int company_id, String company_addr) {
                 this.company_id = company_id;
                 this.company_addr = company_addr;
          }

    public String toString() {

              return "DATA: "+company_id+" "+

    company_addr;

          }

    }


    •       新版本

    import java.io.*;

    public class Serial implements Serializable {

          int company_id;

          String company_addr;

             public Serial1(int company_id, String company_addr) {
                 this.company_id = company_id;
                 this.company_addr = company_addr;
          }

    public String toString() {

              return "DATA: "+company_id+" "+ company_addr;

          }

          public void todo(){}//无关紧要的方法

    }


    首先将老版本序列化,然后用新版本读出,发生错误:

    java.io.InvalidClassException: Serial.Serial1; local class incompatible: stream classdesc serialVersionUID = 762508508425139227, local class serialVersionUID = 1187169935661445676

    接下来我们加入显式的声名UID:

    private static final long serialVersionUID=762508508425139227l;

    再次运行,顺利地产生新对象

    DATA: 1001 com1


    如何保持向上兼容性:

          向上兼容性是指老的版本能够读取新的版本序列化的数据流。常常出现在我们的服务器的数据更新了,仍然希望老的客户端能够支持反序列化新的数据流,直到其更新到新的版本。可以说,这是半自动的事情。

          跟一般的讲,因为在java中serialVersionUID是唯一控制着能否反序列化成功的标志,只要这个值不一样,就无法反序列化成功。但只要这个 值相同,无论如何都将反序列化,在这个过程中,对于向上兼容性,新数据流中的多余的内容将会被忽略;对于向下兼容性而言,旧的数据流中所包含的所有内容都 将会被恢复,新版本的类中没有涉及到的部分将保持默认值。利用这一特性,可以说,只要我们认为的保持serialVersionUID不变,向上兼容性是 自动实现的。

          当然,一但我们将新版本中的老的内容拿掉,情况就不同了,即使UID保持不变,会引发异常。正是因为这一点,我们要牢记一个类一旦实现了序列化又要保持向上下兼容性,就不可以随随便便的修改了!!!

          测试也证明了这一点,有兴趣的读者可以自己试一试。


    如何保持向下兼容性:

             一如上文所指出的,你会想当然的认为只要保持serialVersionUID不变,向下兼容性是自动实现的。但实际上,向下兼容要复杂一些。这是因为,我们必须要对那些没有初始化的字段负责。要保证它们能被使用。

          所以必须要利用
          private void readObject(java.io.ObjectInputStream in)
          throws IOException, ClassNotFoundException{
             in.defaultReadObject();//先反序列化对象
             if(ver=5552){//以前的版本5552
                 …初始化其他字段
              }else if(ver=5550){//以前的版本5550
                …初始化其他字段
              }else{//太老的版本不支持
              throw new InvalidClassException();
          }
    }


          细心的读者会注意到要保证in.defaultReadObject();能够顺利执行,就必须要求serialVersionUID保持一致,所以这里 的ver不能够利用serialVersionUID了。这里的ver是一个我们预先安插好的final long ver=xxxx;并且它不能够被transient修饰。所以保持向下的兼容性至少有三点要求:

               1.serialVersionUID保持一致
               2.预先安插好我们自己的版本识别标志的final long ver=xxxx;
               3.保证初始化所有的域


    讨论一下兼容性策略:

             到这里我们可以看到要保持向下的兼容性很麻烦。而且随着版本数目的增加。维护会变得困难而繁琐。讨论什么样的程序应该使用怎么样的兼容性序列化策略已经超 出本文的范畴,但是对于一个游戏的存盘功能,和对于一个字处理软件的文档的兼容性的要求肯定不同。对于rpg游戏的存盘功能,一般要求能够保持向下兼容, 这里如果使用java序列化的方法,则可根据以上分析的三点进行准备。对于这样的情况使用对象序列化方法还是可以应付的。对于一个字处理软件的文档的兼容 性要求颇高,一般情况下的策略都是要求良好的向下兼容性,和尽可能的向上兼容性。则一般不会使用对象序列化技术,一个精心设计的文档结构,更能解决问题。

    ps:serialVersionUID做为序列化的版本控制是一个非常有用的兼容手段,通常情况下,我们应该手工设置该值,当然,ide eclipse有提示你设置其值。serialVersionUID可以任意设置,根据不同的兼容性做相应改动。


    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2019-12-31 08:59
  • 签到天数: 975 天

    连续签到: 1 天

    [LV.10]测试总司令

    2#
    发表于 2020-12-28 09:16:10 | 只看该作者
    兼容性比较难测
    回复 支持 反对

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-14 22:56 , Processed in 0.066109 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表