在Java中,静态和瞬态字段未序列化。但是,我发现静态字段的初始化会导致生成的serialVersionUID被更改。例如,static int MYINT = 3;
会导致serialVersionUID发生更改。在这个例子中,它是有道理的,因为该类的不同版本将获得不同的初始值。为什么任何初始化都会改变serialVersionUID?例如,static String MYSTRING = System.getProperty("foo");
也会导致serialVersionUID发生变化。
具体来说,我的问题是为什么用方法初始化会导致serialVersionUID发生变化。我遇到的问题是我添加了一个用系统属性值(getProperty)初始化的新静态字段。该更改导致远程调用上的序列化异常。
答案 0 :(得分:5)
您可以在bug 4365406和algorithm for computing serialVersionUID中找到有关该信息的一些信息。基本上,当使用static
更改System.getProperty()
成员的初始化时,编译器会在引用static
类的类中引入新的System
属性(我假设{{1} 1}}类以前在类中没有引用过),并且由于编译器引入的这个属性不是私有的,所以它参与System
计算。
道德:始终使用显式serialVersionUID
,您将节省一些CPU周期和一些麻烦:)
答案 1 :(得分:2)
自动serialVersionUID是根据类的成员计算的。可以使用Sun JDK中的javap工具显示这些类文件。
在问题中提到的情况下,添加/删除的成员是静态初始化程序。这在类文件中显示为()V。可以使用javap -c对该方法的内容进行反汇编。您应该能够对MYSTRING进行System.getProperty(“foo”)调用和赋值。但是,类文件直接支持带有字符串文字(或Java语言规范定义的任何编译时常量)的赋值,因此无需静态初始化。
代码定位J2SE 1.4(使用-source 1.4 -target 1.4)或更早版本的常见情况是旧类实例的静态字段,它们在源代码(MyClass.class)中显示为类文字。使用Class.forName按需查找Class实例,并将其存储在静态字段中。正是这个静态字段破坏了serialVersionUID。从J2SE 5.0开始,ldc操作码的变体直接支持类文字,无需合成字段。同样,所有这些都可以用javap -c显示。
答案 2 :(得分:0)
如果我正确阅读规范,如果更改瞬态字段静态值,则自动serialVersionUID
不应更改。看一下Spec的Chapter 5.6
然而,如果您考虑一下这一点 - 首先序列化具有static int MYINT = 3
的对象,然后当您反序列化该类时,您希望获得相同的对象,是MYINT = 3
。因此,如果您更改静态初始化,您会期望serialVersionUID
更改,因为您无法再次获取相同的对象。
无论如何,将其保留在所有可序列化的类中,您可以控制serialVersionUID
:
private static final long serialVersionUID = 7526472295622776147L;
答案 3 :(得分:0)
我更新问题更清楚了。我理解为什么用文字进行初始化会改变serialVersionUID
,而不是为什么动态初始化会改变它。如果使用方法初始化,则值当然可能总是不同。
只有在您确定安全更改时,才能在后续版本的版本中明确设置serialVersionUID
。