15.8.7 版本

15.8.7 版本

根据前面的介绍可以知道,反序列化Java对象时必须提供该对象的class文件,现在的问题是,随着项目的升级,系统的class文件也会升级,Java如何保证两个class文件的兼容性?

版本serialVersionUID

Java序列化机制允许为序列化类提供一个private static finalserialVersionUID值,该类变量的值用于标识该Java类的序列化版本,也就是说,如果一个类升级后,只要它的serialVersionUID类变量值保持不变,序列化机制也会把它们当成同一个序列化版本
分配serialVersionUID类变量的值非常简单,例如下面代码片段:

1
2
3
4
5
6
public class Test
{
//为该类指定一个serialVersionUID类变量值
private static final long serialversionUID=512L;
...
}

为了在反序列化时确保序列化版本的兼容性,最好在每个要序列化的类中加入private static final long serialVersionUID这个类变量,具体数值自己定义。这样,即使在某个对象被序列化之后,它所对应的类被修改了,该对象也依然可以被正确地反序列化。

不显示指定版本可能造成序列化失败

如果不显式定义serialversionUID类变量的值,该类变量的值将由JVM根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败.

使用serialver.exe生成版本

可以通过JDK安装路径的bin目录下的serialver.exe工具来获得该类的serialVersionUID类变量的值,该命令帮助文档如下:

1
2
PS G:\Desktop\codes\15\15.8\externalizable> serialver
用法: serialver [-classpath 类路径] [-show] [类名称...]

serialver依赖于.class文件,所以先编译得到.class文件:

1
javac -encoding utf-8 Person.java

然后输入命令:

1
serialver person

运行该命令,输出结果如下:

1
Person: static final long serialVersionUID =-3719034423096421849L;
1
2
3
PS G:\Desktop\codes\15\15.8\externalizable> javac -encoding utf-8 Person.java
PS G:\Desktop\codes\15\15.8\externalizable> serialver Person
Person: private static final long serialVersionUID = -3719034423096421849L;

上面的-3719034423096421849L就是系统为该Person类生成的serialVersionUID类变量的值。如果在运行serialver命令时指定-show选项(不要跟类名参数),即可启动如图15.15所示的图形用户界面。
这里有一张图片

不指定版本不利于程序移植

不显式指定serialVersionUID类变量的值的另一个坏处是,不利于程序在不同的JVM之间移植。因为不同的编译器对该类变量的计算策略可能不同,从而造成虽然类完全没有改变,但是因为JVM不同,也会出现序列化版本不兼容而无法正确反序列化的现象
如果类的修改确实会导致该类反序列化失败,则应该为该类的serialVersionUID类变量重新分配值。

类的哪些修改可能导致该类实例的 反序列化失败

那么对类的哪些修改可能导致该类实例的反序列化失败呢?下面分三种情况来具体讨论

方法的修改 不需要修改版本

如果修改类时仅仅修改了方法,则反序列化不受任何影响,类定义无须修改serialVersionUID类变量的值。

静态变量和瞬态变量的修改 不需要修改版本

如果修改类时仅仅修改了静态变量或瞬态实例变量,则反序列化不受任何影响,类定义无须修改serialVersionUID类变量的值。

修改非瞬态实例变量 可能要修改版本

  • 如果修改类时修改了非瞬态的实例变量,则可能导致序列化版本不兼容。
    • 如果对象流中的对象新类包含同名的实例变量,而实例变量类型不同,则反序列化失败,类定义应该更新serialVersionUID类变量的值。(修改实例变量的类型后应该更新版本)
    • 如果对象流中的对象新类包含更多的实例变量,则多出的实例变量值被忽略,序列化版本可以兼容,类定义可以不更新serialVersionuID类变量的值(减少实例变量可以不更新版本)
    • 如果新类对象流中的对象包含更多的实例变量,则序列化版本也可以兼容,类定义可以不更新serialversionUID类变量的值;但反序列化得到的新对象中多出的实例变量值都是null(引用类型实例变量)或0(基本类型实例变量)。(增加新的实例变量可以不更新版本)