15.8.7 版本
15.8.7 版本
根据前面的介绍可以知道,反序列化Java
对象时必须提供该对象的class
文件,现在的问题是,随着项目的升级,系统的class
文件也会升级,Java
如何保证两个class
文件的兼容性?
版本serialVersionUID
Java
序列化机制允许为序列化类提供一个private static final
的serialVersionUID
值,该类变量的值用于标识该Java
类的序列化版本,也就是说,如果一个类升级后,只要它的serialVersionUID
类变量值保持不变,序列化机制也会把它们当成同一个序列化版本。
分配serialVersionUID
类变量的值非常简单,例如下面代码片段:
1 | public class Test |
为了在反序列化时确保序列化版本的兼容性,最好在每个要序列化的类中加入private static final long serialVersionUID
这个类变量,具体数值自己定义。这样,即使在某个对象被序列化之后,它所对应的类被修改了,该对象也依然可以被正确地反序列化。
不显示指定版本可能造成序列化失败
如果不显式定义serialversionUID
类变量的值,该类变量的值将由JVM
根据类的相关信息计算,而修改后的类的计算结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败.
使用serialver.exe生成版本
可以通过JDK
安装路径的bin
目录下的serialver.exe
工具来获得该类的serialVersionUID
类变量的值,该命令帮助文档如下:
1 | PS G:\Desktop\codes\15\15.8\externalizable> serialver |
serialver
依赖于.class
文件,所以先编译得到.class
文件:
1 | javac -encoding utf-8 Person.java |
然后输入命令:
1 | serialver person |
运行该命令,输出结果如下:
1 | Person: static final long serialVersionUID =-3719034423096421849L; |
1 | PS G:\Desktop\codes\15\15.8\externalizable> javac -encoding utf-8 Person.java |
上面的-3719034423096421849L
就是系统为该Person
类生成的serialVersionUID
类变量的值。如果在运行serialver
命令时指定-show
选项(不要跟类名参数),即可启动如图15.15所示的图形用户界面。
不指定版本不利于程序移植
不显式指定serialVersionUID
类变量的值的另一个坏处是,不利于程序在不同的JVM
之间移植。因为不同的编译器对该类变量的计算策略可能不同,从而造成虽然类完全没有改变,但是因为JVM
不同,也会出现序列化版本不兼容而无法正确反序列化的现象
如果类的修改确实会导致该类反序列化失败,则应该为该类的serialVersionUID
类变量重新分配值。
类的哪些修改可能导致该类实例的 反序列化失败
那么对类的哪些修改可能导致该类实例的反序列化失败呢?下面分三种情况来具体讨论
方法的修改 不需要修改版本
如果修改类时仅仅修改了方法,则反序列化不受任何影响,类定义无须修改serialVersionUID
类变量的值。
静态变量和瞬态变量的修改 不需要修改版本
如果修改类时仅仅修改了静态变量或瞬态实例变量,则反序列化不受任何影响,类定义无须修改serialVersionUID
类变量的值。
修改非瞬态实例变量 可能要修改版本
- 如果修改类时修改了非瞬态的实例变量,则可能导致序列化版本不兼容。
- 如果对象流中的对象和新类中包含同名的实例变量,而实例变量类型不同,则反序列化失败,类定义应该更新
serialVersionUID
类变量的值。(修改实例变量的类型后应该更新版本) - 如果对象流中的对象比新类中包含更多的实例变量,则多出的实例变量值被忽略,序列化版本可以兼容,类定义可以不更新
serialVersionuID
类变量的值(减少实例变量可以不更新版本) - 如果新类比对象流中的对象包含更多的实例变量,则序列化版本也可以兼容,类定义可以不更新
serialversionUID
类变量的值;但反序列化得到的新对象中多出的实例变量值都是null
(引用类型实例变量)或0(基本类型实例变量)。(增加新的实例变量可以不更新版本)
- 如果对象流中的对象和新类中包含同名的实例变量,而实例变量类型不同,则反序列化失败,类定义应该更新