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