为什么编译器不自动设置 serialVersionUID?

Why compiler do not set serialVersionUID automatically?

如果Serializable class没有定义serialVersionUID,有风险(!对于同一个class!)进行序列化的JVM会计算不同的版本比做反序列化的JVM。推荐的解决方案是使用 serialver 工具计算版本 ID 并手动放入源代码。

恕我直言,这很愚蠢。如果源代码中没有定义,javac 自动计算 serialVersionUID 并放入字节码会更好。

编译时自动计算是否有问题?

我知道有类似编译器插件的东西 javac -Xplugin。是否可以创建执行自动化的插件?或者可能已经创建了这样的插件?

请不要给我 Why isn't the serialVersionUID automatically generated? 的虚假副本 讨论了 classes 的不同版本兼容序列化的情况。我很感兴趣的一个案例是,相同的 class 应该总是通过序列化,而不同版本的 class 应该会失败。

因为ObjectInputStream和朋友一起做。它不是属于编译器的功能,即使它解决了也解决不了任何问题。编译器不能做任何与已经完成的不同的事情。

I'm interested in a case when the same class is expected to always pass serialization and different versions of class should fail.

那个案例已经奏效了。无需解决方案。

If Serializable class has no serialVersionUID defined, there is a risk that (!for the same class!) JVM which do serialization will calculate different version than JVM which do deserialization.

据我所知,它指定了如何计算默认的 serialVersionUID 并且每个兼容的 JVM 都应该正确地实现它:http://docs.oracle.com/javase/8/docs/platform/serialization/spec/class.html#a4100

In doc you linked is a sentence: Note - It is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected serialVersionUID conflicts during deserialization, causing deserialization to fail. So if 'every compliant JVM should implement this correctly' there is won't be unexpected serialVersionUID conflicts. But there are

不,它 对 class 细节高度敏感,这些细节可能因编译器实现而异 ,但对于给定的 class 文件 class details 一旦编译就不会改变。但是,如果您使用两个不同的编译器编译同一个 java 文件,您最终可能会得到两个具有不同默认 serialVersionUID 的 class 文件。

如果编译器将默认的 serialVersionUID 添加到 class 文件,在这方面不会有任何改变。

所以 Serializable class Javadoc 状态:

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value.

现在 Java Serialization Specification 状态:

The serialVersionUID is computed using the signature of a stream of bytes that reflect the class definition. The National Institute of Standards and Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a signature for the stream. The first two 32-bit quantities are used to form a 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data types to a sequence of bytes. The values input to the stream are defined by the Java Virtual Machine (VM) specification for classes. Class modifiers may include the ACC_PUBLIC, ACC_FINAL, ACC_INTERFACE, and ACC_ABSTRACT flags; other flags are ignored and do not affect serialVersionUID computation. Similarly, for field modifiers, only the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when computing serialVersionUID values. For constructor and method modifiers, only the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags are used. Names and descriptors are written in the format used by the java.io.DataOutputStream.writeUTF method.

因此 serialVersionUID 是在运行时基于 "the signature of a stream of bytes that reflect the class definition" 计算的,因此基于编译器的输出。我相信这就是为什么他们说 "it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations".

但是无论如何,如果您使用相同的 JDK 运行时、相同的编译器等。这种情况不应该发生(当然,除非 JDK 中存在错误)。