我什么时候需要更改serialVersionUID?

Kyl*_*yle 48 java serialization

我知道我可以使用serialVersionUID来控制类的版本.我读到我可以添加或删除字段,类仍然兼容,它只使用默认值.

什么时候必须更改serialVersionUID?

Vin*_*lds 52

理想情况下,当对类的结构进行不兼容的更改时,应更改serialVersionUID字段的值.Java对象序列化规范中列出了不兼容更改的完整列表.

要进一步扩展,对类的不兼容更改将阻止反序列化机制创建对象的实例,因为流中的信息未映射到当前类定义.

  • *为了进一步扩展,不兼容的更改 [...] 阻止反序列化机制创建实例*:这个答案是否等于说我应该更改 `serialVersionUID` 以使反序列化机制抛出异常,即使它已经可以在没有我帮助的情况下这样做,因为这是一个不兼容的更改,阻止了上述机制的工作?如果是这种情况,我认为应该澄清一下这有什么用处。(我在这里怀疑是因为 [EJP 的回答](/sf/answers/230179631/) 更有意义。) (2认同)

use*_*421 22

关于serialVersionUID每次改变课程时改变的经常重复的口头禅是完整的,完全是胡说八道.查看他们在其网站上重新发布的Sun文章,该文章在收购后迁移到Oracle技术网.

你应该改变的serialVersionUID 只有当你刻意要打破所有现有的序列化的兼容性,例如,当改变你的类将使它所以语义不同,你别无选择-在这种情况下,你应该认真考虑一下它是什么,好几次你其实在做.

在所有其他情况下,您应该尝试使用自定义readObject()/writeObject()和/或writeReplace()/readResolve()方法和/或serialFields注释来破坏锅炉,以便您可以继续从这些现有序列中读取对象.一旦你打破了你的主要头痛,确实是噩梦.

  • 如果我向类添加一个新字段怎么办?此更改仍然兼容:不会引发异常。但该字段不会包含任何信息,因此在语义上它是不兼容的。在这种情况下我应该增加 UID 吗? (2认同)
  • @damluar不会.这只会让***更加***不相容.至少如果你没有导致异常,你有几个修补语义的机会.除了你没有希望. (2认同)

Tom*_*m G 7

如果你没有serialVersionUID在你的Serializable类中指定一个字段,Java 编译器会为你指定一个——本质上它是类名、接口名、方法和类字段的散列。但是,方法可以随时更改,因此如果您需要更改存储类的反序列化方式,则可以覆盖 readObject 方法。但是,如果您确实serialVersionUID在代码中指定了该字段,即使您进行了不兼容的更改,编译器也不会覆盖该字段,这可能会导致运行时出现异常——您的 IDE 或编译器不会向您发出警告。(编辑 - 感谢 EJP)如果您想轻松检查编译器如何查看某些更改,Eclipse 等 IDE 可以为您插入编译器的 UID。

如果您经常进行更改,请保留旧版本的磁盘文件以测试反序列化。您可以编写单元测试来尝试读取旧文件,看看它是否有效或者是否完全不兼容。

一个警告,我个人经历过使用Serializable最初用于长期存储但设计不当的类的痛苦。例如,将 GUI 元素存储在磁盘上而不是在需要时创建它们。问问自己这是否Serializable真的是保存数据的最佳方式。


Mal*_*alt 5

为了完整起见,下面列出了根据java 8 规范破坏 Java 序列化兼容性的更改:

  • 删除字段
  • 在层次结构中向上或向下移动类
  • 将非静态字段更改为静态字段或将非瞬态字段更改为瞬态字段
  • 更改原始字段的声明类型
  • 更改 writeObject 或 readObject 方法,使其不再写入或读取默认字段数据,或更改它,以便在以前的版本没有写入或读取时尝试写入或读取它。
  • 将类从可序列化更改为可外部化,反之亦然
  • 将类从非枚举类型更改为枚举类型,反之亦然
  • 删除可序列化或可外部化
  • 将 writeReplace 或 readResolve 方法添加到类中