10.6.4 版本更新

在介绍自定义序列化格式时提到了由于版本更新带来的序列化格式不兼容的问题。版本更新所带来的不兼容问题是程序开发中的常见问题。版本升级之后,可能造成之前的程序无法正确运行。不过,如果程序中使用了序列化机制,兼容性的问题就会更多。旧版本的Java对象序列化后的字节流通常会保存在磁盘文件或数据库之中。这些持久存储中的数据的保存时间一般都很长,而且都记录了程序运行的历史数据。当版本更新之后,要求这些历史数据仍然是可用的。程序在更新过程中应该尽可能保证序列化格式的兼容性。但是在有些情况下,可能无法做到完全兼容,可以选择从某些版本开始不再与之前的版本保持兼容。一般来说,在新的版本中添加域不会产生什么问题,而删去一些域或改变已有域的行为则不行。

在使用ObjectInputStream类的对象读取一个旧版本的Java对象的序列化结果时,会尝试在当前虚拟机中查找其Java类的定义。如果找不到类的定义,就尝试加载该Java类,被加载的有可能是新版本的Java类。需要一种方式来判断旧版本的序列化内容是否与当前的Java类兼容。如果不兼容,那么读取操作会直接失败。这种兼容性的判断是通过Java类中的全局唯一标识符serialVersionUID来实现的。当Java类实现了Serializable接口时,需要声明该Java类唯一的序列化版本号。这个版本号会被包括在序列化后的字节流中。在进行读取时,会比较从字节流中得到的版本号与对应Java类中声明的版本号是否一致,以确定两者是否兼容。只有在版本一致时,序列化操作才能完成。代码清单10-18和代码清单10-19中的两个NewUser类的serialVersionUID域的值均为1,说明这两个类在序列化格式上是兼容的。如果NewUser类的后续更新造成了序列化格式不再兼容,那么需要把serialVersionUID域设置成另外一个不同的值。

如果Java类实现了Serializable接口,但是没有显式地声明serialVersionUID域,那么虚拟机会根据Java类的各种元素的特征计算出一个散列值,作为serialVersionUID域的值。默认生成的serialVersionUID域的值会随着Java类的更新而发生变化。如果使用默认的序列化格式,那么默认生成的serialVersionUID域是合理的。如果使用自定义的序列化格式,则通常要显式地声明serialVersionUID域的值。可以通过Java提供的serialver命令行工具生成一个Java类的serialVersionUID域的值。在Eclipse中,如果Java类实现了Serializable接口,那么Eclipse会提示并辅助生成这个serialVersionUID域。