显式的serialVersionUID 被认为是有害的?

Posted

技术标签:

【中文标题】显式的serialVersionUID 被认为是有害的?【英文标题】:explicit serialVersionUID considered harmful? 【发布时间】:2010-09-30 00:09:27 【问题描述】:

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

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

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

评论?

【问题讨论】:

【参考方案1】:

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

Eclipse 默认警告没有serialVersionUID,这很烦人且具有误导性 这曾经也是我的观点,直到大约一个月前,当我被告知 Jon Skeet 指出的问题时。这对您的运行时施加了其他限制,以确保序列化正常工作。 Robin:我想这是优先级问题。我们更害怕奇怪的错误,而不是切换编译器进行调试。【参考方案3】:

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

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

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

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

简而言之,使用serialversionuid一般来说危害更大。

【讨论】:

我不认为JVM版本有问题。如果您没有指定,serialVersionUID 由编译器创建。【参考方案4】:

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

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

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

【讨论】:

就像你说的,要提供长期的反序列化能力,还有很多工作要做,可能是版本特殊的反序列化例程等。感谢您的回复。【参考方案5】:

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

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

【讨论】:

谢谢。对于任何严重的序列化,我怀疑不会使用 Java 序列化。例如,对于数据库持久性,通常有一个升级脚本和大量测试。【参考方案6】:

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

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

【讨论】:

以上是关于显式的serialVersionUID 被认为是有害的?的主要内容,如果未能解决你的问题,请参考以下文章

java中的serialVersionUID。 如果在本地的两个类中声明了两个值相同的这个变量,会有啥问题吗?

为啥从 main() 显式返回 0 被认为是好的做法? [复制]

PL/SQL 游标的使用详解

在不使用显式锁的方式下使用多线程

如果父类实现了Serializable并生成了serialVersionUID,而子类并没有写重新生成serialVersionUID的语句?

serialVersionUID