在定义一个实现了序列化接口的类时,有一个问题一直困扰着初学者,是否需要定义序列化版本号?如果不定义,在Elclipse和Idea等开发工具中就会出现警告信息;如果定义,那么这个版本号的用途是什么?它是怎样产生的呢?
其实,序列化版本号在实际开发中是非常必要的。因为它能够保证类在发生合理改动的情况下不会影响对象的反序列化;而在漫长的开发周期中,很难保证对已经定义的类文件不发生调整和修改:可能添加或者删除某些数据域和方法。在这种情况下,定义序列化版本号,可以确保反序列化成功。
那么,序列化版本号是什么呢?序列化版本号是类的最初版本的指纹。所谓指纹就是表示类的字节码的唯一标识,它通常占用8个字节。Java使用 SHA算法计算类的指纹,SHA算法保证类文件发生任何形式的变化它的指纹也会发生改变。通常JVM在加载类文件形成字节码时会计算类的指纹,并在实施序列化操作时,将类的指纹写入到序列化文件中,具体的位置是放置在类描述符之后。在之后的反序列化过程中,JVM根据序列化文件中的类描述符加载运行环境下的类文件并再次计算类的指纹;如果类的指纹与序列化文件中的指纹相同,则反序列化成功;否则反序列化就会失败。如果在实施对象的序列化操作之后,产生对象的类文件发生了变化,而又希望反序列化时能够成功,就需要明确的在类文件中设置版本号。这样,由于变化所形成的类文件的后续版本实际上使用的还是最初的指纹。由于显示的在类文件中定义了序列化版本号,JVM在加载类时就不会计算新的指纹而是使用最初的指纹,因此就和序列化文件中保存的指纹相同,从而就不会导致反序列化的失败。举例说明上述过程:
- 定义序列化对象所对应的类时,没有使用序列化版本号定义Student类,包含name、male、gap三个数据域,产生的对象需要序列化
定义测试类01,实施序列化
控制台执行结果如下:
定义测试类02,实施反序列化
控制台执行结果如下:
如果在实施反序列化之前,修改Student类,删除male数据域后,再实施反序列化时,就会失败。Student类修改如下:
实施反序列化后,在控制台的输出结果如下:
控制台的输出信息表明反序列化产生异常的原因是:JVM在加载类文件时生成的序列号(local class serialVersionUID)与序列化文件记录的序列号(stream classdesc serialVersionUID)不一致。这说明一旦类文件发生了变动,比如删除属性,修改方法等,JVM就需要重新计算指纹,从而产生新的版本号。