显式的serialVersionUID被认为是有害的?
在我看来,为新类显式指定serialVersionUID 是不好的。 考虑两种情况:当布局应该改变时不改变它,以及当布局不应该改变时改变它。
几乎只有当它是明确的时候,才应该改变而没有改变。 在这种情况下,它会导致一些非常微妙、难以发现的错误。 特别是在开发过程中,类布局经常发生变化。 但如果没有明确指定,它就会发生变化,并且反序列化会严重中断,很可能通过清除存储库来解决。
几乎只有当它是隐式的时候才会发生不应该改变的情况。 这是类布局已更改但我们仍然希望从旧的序列化 blob 进行反序列化的罕见情况。 这可能会在 QA 期间被捕获(从 5.2 升级到 5.2.1 后出现奇怪的错误,请参阅附加的堆栈跟踪),并且可以通过设置显式值来轻松修复。
评论?
It seems to me that explicitly specifying serialVersionUID for new classes is bad. Consider the two cases of not changing it when layout has it should have been changed and changing it when it should not have.
Not changing when it should have been changed occurs almost only when it is explicit. In this case, it results in some very subtle, hard-to-find bugs. Especially during development, when class layout changes often. But if it has not been explicitly specified, it will change and the deserialization will break loudly, mostly likely solved by purging the repository.
Changing it when should not have would occur almost only when it is implicit. This is the rare case where class layout has changed but we still want to deserialize from the old serialized blobs. This will likely be caught during QA (Strange errors after upgrade from 5.2 to 5.2.1, see attached stack trace) and can be trivially fixed by setting a explicit value.
Comments?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
在我的工作中,我们明确禁止指定serialVersionUID,正是因为您提出的问题。
另外,我们持久化的类只是用来存储数据,内部没有任何逻辑,所以它们改变的唯一方式就是改变数据成员。
At my work we explicitly prohibit specifying serialVersionUID, exactly because of the problems you bring up.
In addition, the classes we persist are only used to store data with no logic inside, so the only way they change is because of changing data members.
进一步强调 john skeet 所说的内容并反驳评论:
“如果您不需要(即您总是使用相同的类版本序列化和反序列化),那么您可以安全地跳过显式声明”
即使您是没有长期序列化并且使用相同的类版本,您仍然可能遇到问题。 如果您正在编写客户端-服务器代码,并且客户端代码可以使用与服务器不同的 jvm 版本/实现来运行,那么您可能会遇到与不兼容的串行版本 uid 相同的问题。
总而言之,不指定serialversionuids的唯一时间是当您不长期序列化并且保证序列化数据的所有使用者将使用相同的jvm实现并且作为原始制作人的版本。
简而言之,不使用serialversionuid通常是更有害的情况。
to further emphasize what john skeet said and to contradict the comment:
"If you don't need that (i.e. you always serialize and de-serialize with the same class version), then you can safely skip the explicit declaration"
Even if you are not serializing long-term and are using the same class version, you could still have issues. if you are writing client-server code and the client code could run w/ a different jvm version/implementation than the server you can have the same problems with incompatible serialversionuids.
to summarize, the only time it is "safe" to not specify serialversionuids is when you are not serializing long-term and you are guaranteed that all consumers of the serialized data will be using the same jvm implementation and version as the original producer.
in short, not using serialversionuid is generally the more harmful situation.
当您需要通过序列化支持长时间持久化时,您几乎总是需要使用自定义代码来支持这一点,并且需要显式设置
serialVersionUID
,否则旧的序列化版本将无法反序列化通过较新的代码。当类更改时,这些场景已经需要非常小心才能使所有情况正确,因此serialVersionUID是您遇到的问题最少的。
如果您不需要(即始终使用相同的类版本进行序列化和反序列化),那么您可以安全地跳过显式声明,因为计算值将确保使用正确的版本。
When you need to support long-time persistence via serialization, then you almost always need to use custom code to support this and need to explicitly set the
serialVersionUID
, as otherwise older serialized version will not be de-serializable by newer code.Those scenarios already require a great deal of care in order to get all the cases correct, when the class changes, so the serialVersionUID is the least of your problems.
If you don't need that (i.e. you always serialize and de-serialize with the same class version), then you can safely skip the explicit declaration, as the computed value will make sure that the correct version is used.
无论您是否选择
serialVersionUID
(我建议您这样做),那么您都应该考虑创建一套全面的串行兼容性测试。谨慎设计串行格式也是值得的。 它实际上是一个公共 API。
Whether you go for
serialVersionUID
or not (I suggest you do), then you should really consider creating a comprehensive set of tests for serial compatibility.It's also worth designing the serial format with care. It is effectively a public API.
如果您只是将序列化用于远程方法调用,例如调用 EJB,其中客户端和服务器类以及 jvm 相同,我怀疑这是迄今为止最常见的用途,那么显式设置serialVersionUID(例如 eclipse建议)可能会给您带来巨大的痛苦,其形式是偶尔的、无法解释的错误,其中不兼容的类实例由于固定的serialVersionUID而被视为兼容。 在低级序列化期间,远程调用会默默地出错,并且只有当对象的状态不一致时,问题才会在稍后出现。 只有当您意识到客户端和服务器类在某种程度上有所不同(尽管serialVersionUID 当然不是)时,您才能找到问题的根源。 根据我的经验,因此设置serialVersionUID弊大于利。
另一方面,如果您显式设置serialVersionUID以读取旧数据,则根据定义,您正在读取不兼容的版本,并且可能最终得到处于不一致或不完整状态的对象。 在这种情况下,设置serialVersionUID是解决另一个问题的解决方法。
If you're just using serialization for a remote method call, e.g. calling an EJB, where the client and server classes and jvm are the same, which I suspect is by far the most common use, then setting serialVersionUID explicitly (as for example eclipse suggests) is likely to cause you significant pain in form of occasional, inexplicable bugs where incompatible class instances are treated as compatible because of the fixed serialVersionUID. Remote calls will silently go wrong during low-level serialization and the problem will only crop up later when your object's state is inconsistent. You find the source of the problem only when you realize your client and server classes are somehow different (though serialVersionUID is of course not). In my experience setting serialVersionUID for this reason does more harm than good.
If, on the other hand, you explicitly set serialVersionUID to read in old data, you are by definition reading in an incompatible version and are likely to end up with an object in an inconsistent or incomplete state. In this case setting serialVersionUID is a workaround for a different problem.
在不应该发生的情况下进行更改可能会由于类布局更改以外的原因而发生 - 问题在于它依赖于编译器实现。 如果您使用 Eclipse 进行调试,但使用 javac 进行生产构建,那么您可能会得到两组不兼容的数据。
Changing when it shouldn't may happen for reasons other than class layout changes - the problem is that it's compiler implementation dependent. If you do debug with Eclipse but do production builds with javac, you may end up with two incompatible sets of data.