25

在我看来,为新类明确指定 serialVersionUID 是不好的。考虑两种情况,当布局有它应该改变时不改变它,当它不应该改变它时改变它。

当它应该被改变时不改变几乎只发生在它是明确的时候。在这种情况下,它会导致一些非常微妙、难以发现的错误。特别是在开发过程中,当类布局经常变化时。但是如果没有明确指定,它会改变并且反序列化会大声中断,很可能通过清除存储库来解决。

几乎只有在它是隐式的时候才会在不应该的时候改变它。这是类布局已更改但我们仍希望从旧的序列化 blob 中反序列化的罕见情况。这可能会在 QA 期间被捕获(从 5.2 升级到 5.2.1 后出现奇怪的错误,请参阅附加的堆栈跟踪),并且可以通过设置显式值来轻松修复。

评论?

4

6 回答 6

21

除了类布局更改之外的其他原因可能会更改不应该发生的时间 - 问题在于它依赖于编译器实现。如果您使用 Eclipse 进行调试,但使用 javac 进行生产构建,您最终可能会得到两组不兼容的数据。

于 2009-01-07T10:05:49.827 回答
5

在我的工作中,我们明确禁止指定serialVersionUID,正是因为您提出的问题。

另外,我们持久化的类只是用来存储数据,里面没有逻辑,所以唯一的改变就是因为改变了数据成员。

于 2009-01-07T12:19:58.450 回答
4

进一步强调 john skeet 所说的话并反驳评论:

“如果你不需要那个(即你总是使用相同的类版本进行序列化和反序列化),那么你可以安全地跳过显式声明”

即使您没有长期序列化并且使用相同的类版本,您仍然可能遇到问题。如果您正在编写客户端-服务器代码,并且客户端代码可以使用与服务器不同的 jvm 版本/实现运行,那么您可能会遇到与不兼容的 serialversionuids 相同的问题。

总而言之,唯一不指定 serialversionuids 是“安全”的情况是当您不进行长期序列化并且保证序列化数据的所有消费者将使用与原始生产者相同的 jvm 实现和版本时。

简而言之,使用serialversionuid通常是更有害的情况。

于 2009-01-07T15:26:34.780 回答
2

当您需要通过序列化支持长期持久性时,您几乎总是需要使用自定义代码来支持这一点,并且需要显式设置serialVersionUID,否则较旧的序列化版本将无法被较新的代码反序列化。

当类发生变化时,这些场景已经需要非常小心才能使所有案例都正确,因此 serialVersionUID 是您的问题中最少的。

如果您不需要(即您总是使用相同的类版本进行序列化和反序列化),那么您可以安全地跳过显式声明,因为计算值将确保使用正确的版本。

于 2009-01-07T10:06:21.650 回答
1

无论您是否选择serialVersionUID(我建议您这样做),您都应该真正考虑为串行兼容性创建一套全面的测试。

仔细设计串行格式也是值得的。它实际上是一个公共 API。

于 2009-01-07T12:13:53.047 回答
1

如果您只是将序列化用于远程方法调用,例如调用 EJB,其中客户端和服务器类以及 jvm 是相同的,我怀疑这是迄今为止最常见的用途,然后显式设置 serialVersionUID(例如 eclipse建议)可能会以偶然的、莫名其妙的错误的形式给您带来极大的痛苦,其中不兼容的类实例由于固定的 serialVersionUID 而被视为兼容。远程调用在低级序列化过程中会默默地出错,并且只有在对象的状态不一致时才会出现问题。只有当您意识到您的客户端和服务器类在某种程度上不同时,您才能找到问题的根源(尽管 serialVersionUID 当然不是)。根据我的经验,出于这个原因设置 serialVersionUID 弊大于利。

另一方面,如果您明确设置 serialVersionUID 以读取旧数据,则根据定义,您读取的是不兼容的版本,并且可能最终导致对象处于不一致或不完整的状态。在这种情况下,设置 serialVersionUID 是解决不同问题的一种解决方法。

于 2012-01-16T15:20:01.933 回答