8

在 Java 中,静态和瞬态字段不被序列化。但是,我发现静态字段的初始化会导致生成的 serialVersionUID 发生变化。例如,static int MYINT = 3;导致 serialVersionUID 更改。在这个例子中,这是有道理的,因为不同版本的类会得到不同的初始值。为什么任何初始化都会更改 serialVersionUID?例如,static String MYSTRING = System.getProperty("foo");还会导致 serialVersionUID 更改。

具体来说,我的问题是为什么使用方法进行初始化会导致 serialVersionUID 发生变化。我遇到的问题是我添加了一个使用系统属性值(getProperty)初始化的新静态字段。该更改导致远程调用出现序列化异常。

4

4 回答 4

6

您可以在错误 4365406计算serialVersionUID的算法中找到一些相关信息。基本上,当使用 更改static成员的初始化时System.getProperty(),编译器会在引用该类的类中引入一个新static属性System(我假设System该类以前在您的类中未引用),并且由于编译器引入的这个属性不是私有的,它参与serialVersionUID计算。

道德:总是使用显式serialVersionUID,你会节省一些 CPU 周期和一些头痛:)

于 2008-09-03T21:46:04.563 回答
2

自动 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 显示。

于 2008-09-05T15:32:57.873 回答
0

如果我正确阅读了规范,serialVersionUID那么如果您更改瞬态字段的静态值,则自动不应更改。看看规范的第 5.6 章。

但是,如果您稍微考虑一下 - 您首先序列化具有 的对象,static int MYINT = 3然后反序列化您希望返回相同对象的类,即具有MYINT = 3. 因此,如果您更改静态初始化,您会期望serialVersionUID更改,因为您无法再次获取相同的对象。

无论如何,将它保存在所有可序列化的类中,您可以控制serialVersionUID

private static final long serialVersionUID = 7526472295622776147L;
于 2008-09-03T12:01:30.663 回答
0

我更新了问题以更清楚。我理解为什么使用文字进行初始化会改变,serialVersionUID但不理解为什么动态初始化会改变它。如果您使用方法进行初始化,那么值当然可能总是不同的。

serialVersionUID仅当您确定它是安全更改时,才能在该类的后续版本中设置显式。

于 2008-09-03T12:13:09.107 回答