Java 什么是 serialVersionUID,我为什么要使用它?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/285793/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 12:33:03  来源:igfitidea点击:

What is a serialVersionUID and why should I use it?

javaserializationserialversionuid

提问by ashokgelal

Eclipse issues warnings when a serialVersionUIDis missing.

serialVersionUID缺少a 时 Eclipse 会发出警告。

The serializable class Foo does not declare a static final serialVersionUID field of type long

可序列化类 Foo 未声明 long 类型的静态最终 serialVersionUID 字段

What is serialVersionUIDand why is it important? Please show an example where missing serialVersionUIDwill cause a problem.

什么是serialVersionUID以及为什么重要?请举例说明缺少serialVersionUID会导致问题的地方。

采纳答案by Jon Skeet

The docs for java.io.Serializableare probably about as good an explanation as you'll get:

的文档java.io.Serializable可能与您将获得的解释一样好:

The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUIDthan that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUIDexplicitly by declaring a field named serialVersionUIDthat must be static, final, and of type long:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUIDvalue for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommendedthat all serializable classes explicitly declare serialVersionUIDvalues, since the default serialVersionUIDcomputation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptionsduring deserialization. Therefore, to guarantee a consistent serialVersionUIDvalue across different java compiler implementations, a serializable class must declare an explicit serialVersionUIDvalue. It is also strongly advised that explicit serialVersionUIDdeclarations use the private modifier where possible, since such declarations apply only to the immediately declaring class serialVersionUIDfields are not useful as inherited members.

序列化运行时将每个可序列化类与一个称为 a 的版本号相关联,serialVersionUID在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。如果接收者为对象加载了一个serialVersionUID与相应发送者的类不同的类,那么反序列化将导致 InvalidClassException. 一个可序列化的类可以serialVersionUID通过声明一个serialVersionUID必须是 static、final 和 type的字段来显式声明它自己long

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化类未显式声明serialVersionUID,则序列化运行时将根据serialVersionUID该类的各个方面计算该类的默认值,如 Java(TM) 对象序列化规范中所述。但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认serialVersionUID计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能会InvalidClassExceptions在反序列化过程中导致意外。因此,为了保证serialVersionUID不同的 Java 编译器实现的值一致,可序列化的类必须声明一个显式serialVersionUID值。还强烈建议明确serialVersionUID声明在可能的情况下使用 private 修饰符,因为此类声明仅适用于立即声明的类serialVersionUID字段,不能作为继承成员使用。

回答by eishay

If you will never need to serialize your objects to byte array and send/store them, then you don't need to worry about it. If you do, then you must consider your serialVersionUID since the deserializer of the object will match it to the version of object its classloader has. Read more about it in the Java Language Specification.

如果您永远不需要将对象序列化为字节数组并发送/存储它们,那么您无需担心。如果你这样做,那么你必须考虑你的 serialVersionUID,因为对象的反序列化器会将它匹配到它的类加载器所拥有的对象版本。在 Java 语言规范中阅读有关它的更多信息。

回答by Scott Bale

I can't pass up this opportunity to plug Josh Bloch's book Effective Java(2nd Edition). Chapter 11 is an indispensible resource on Java serialization.

我不能错过这个机会来插入 Josh Bloch 的书Effective Java(第二版)。第 11 章是 Java 序列化不可或缺的资源。

Per Josh, the automatically-generated UID is generated based on a class name, implemented interfaces, and all public and protected members. Changing any of these in any way will change the serialVersionUID. So you don't need to mess with them only if you are certain that no more than one version of the class will ever be serialized (either across processes or retrieved from storage at a later time).

根据 Josh,自动生成的 UID 是基于类名、实现的接口以及所有公共和受保护成员生成的。以任何方式更改其中任何一项都会改变serialVersionUID. 因此,仅当您确定不会序列化类的超过一个版本(跨进程或稍后从存储中检索)时,您才需要弄乱它们。

If you ignore them for now, and find later that you need to change the class in some way but maintain compatibility w/ old version of the class, you can use the JDK tool serialverto generate the serialVersionUIDon the oldclass, and explicitly set that on the new class. (Depending on your changes you may need to also implement custom serialization by adding writeObjectand readObjectmethods - see Serializablejavadoc or aforementioned chapter 11.)

如果暂时忽略它们,后来发现需要以某种方式更改类但要保持与旧版本类的兼容性,则可以使用 JDK 工具serialverserialVersionUID类上生成,并显式设置在新班级上。(根据您的更改,您可能还需要通过添加writeObjectreadObject方法来实现自定义序列化- 请参阅Serializablejavadoc 或上述第 11 章。)

回答by matt b

You can tell Eclipse to ignore these serialVersionUID warnings:

您可以告诉 Eclipse 忽略这些 serialVersionUID 警告:

Window > Preferences > Java > Compiler > Errors / Warnings > Potential Programming Problems

窗口 > 首选项 > Java > 编译器 > 错误/警告 > 潜在的编程问题

In case you didn't know, there are a lot of other warnings you can enable in this section (or even have some reported as errors), many are very useful:

如果您不知道,您可以在本节中启用许多其他警告(或者甚至将一些报告为错误),其中许多非常有用:

  • Potential Programming Problems: Possible accidental boolean assignment
  • Potential Programming Problems: Null pointer access
  • Unnecessary code: Local variable is never read
  • Unnecessary code: Redundant null check
  • Unnecessary code: Unnecessary cast or 'instanceof'
  • 潜在的编程问题:可能的意外布尔赋值
  • 潜在的编程问题:空指针访问
  • 不必要的代码:永远不会读取局部变量
  • 不必要的代码:冗余空检查
  • 不必要的代码:不必要的强制转换或“instanceof”

and many more.

还有很多。

回答by MetroidFan2002

If you're serializing just because you have to serialize for the implementation's sake (who cares if you serialize for an HTTPSession, for instance...if it's stored or not, you probably don't care about de-serializinga form object), then you can ignore this.

如果您序列化只是因为您必须为了实现而序列化(例如,谁在乎您是否为 序列化HTTPSession...如果它是否已存储,您可能不关心de-serializing表单对象),那么您可以忽略这个。

If you're actually using serialization, it only matters if you plan on storing and retrieving objects using serialization directly. The serialVersionUIDrepresents your class version, and you should increment it if the current version of your class is not backwards compatible with its previous version.

如果您确实在使用序列化,那么只有您计划直接使用序列化来存储和检索对象时才重要。该serialVersionUID代表你的类版本,你应该增加,如果你的类的当前版本不向后与以前的版本兼容。

Most of the time, you will probably not use serialization directly. If this is the case, generate a default SerialVersionUIDby clicking the quick fix option and don't worry about it.

大多数情况下,您可能不会直接使用序列化。如果是这种情况,请SerialVersionUID通过单击快速修复选项生成默认值,不要担心。

回答by MetroidFan2002

It would be nice if CheckStyle could verify that the serialVersionUID on a class that implements Serializable has a good value, i.e. that it matches what the serial version id generator would produce. If you have a project with lots of serializable DTOs, for example, remembering to delete the existing serialVersionUID and regenerate it is a pain, and currently the only way (that I know of) to verify this is to regenerate for each class and compare to the old one. This is very very painful.

如果 CheckStyle 可以验证实现 Serializable 的类上的 serialVersionUID 是否具有良好的值,即它与串行版本 ID 生成器将生成的内容相匹配,那就太好了。例如,如果您有一个包含大量可序列化 DTO 的项目,请记住删除现有的 serialVersionUID 并重新生成它是一种痛苦,目前(我所知道的)验证这一点的唯一方法是为每个类重新生成并与旧的。这是非常非常痛苦的。

回答by Pa?lo Ebermann

If you get this warning on a class you don't ever think about serializing, and that you didn't declare yourself implements Serializable, it is often because you inherited from a superclass, which implements Serializable. Often then it would be better to delegate to such a object instead of using inheritance.

如果您在一个从未考虑过序列化的类上收到此警告,并且您没有声明自己implements Serializable,这通常是因为您继承自实现了 Serializable 的超类。通常,委托给这样的对象而不是使用继承会更好。

So, instead of

所以,而不是

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

do

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

and in the relevant methods call myList.foo()instead of this.foo()(or super.foo()). (This does not fit in all cases, but still quite often.)

并在相关方法中调用myList.foo()而不是this.foo()(或super.foo())。(这并不适合所有情况,但仍然很常见。)

I often see people extending JFrame or such, when they really only need to delegate to this. (This also helps for auto-completing in a IDE, since JFrame has hundreds of methods, which you don't need when you want to call your custom ones on your class.)

我经常看到人们扩展 JFrame 等,当他们真的只需要委托给它时。(这也有助于在 IDE 中自动完成,因为 JFrame 有数百个方法,当您想在类上调用自定义方法时不需要这些方法。)

One case where the warning (or the serialVersionUID) is unavoidable is when you extend from AbstractAction, normally in a anonymous class, only adding the actionPerformed-method. I think there shouldn't be a warning in this case (since you normally can't reliable serialize and deserialize such anonymous classes anyway accross different versions of your class), but I'm not sure how the compiler could recognize this.

警告(或 serialVersionUID)不可避免的一种情况是,当您从 AbstractAction 扩展时,通常在匿名类中,只添加 actionPerformed-method。我认为在这种情况下不应该有警告(因为您通常无法在您的类的不同版本中可靠地序列化和反序列化此类匿名类),但我不确定编译器如何识别这一点。

回答by grand johnson

Don't bother, the default calculation is really good and suffice for 99,9999% of the cases. And if you run into problems, you can - as already stated - introduce UID's as the need arrise (which is highly unlikely)

别担心,默认计算真的很好,足以满足 99,9999% 的情况。如果您遇到问题,您可以 - 如前所述 - 在需要时引入 UID(这是极不可能的)

回答by Alexander Torstling

serialVersionUIDfacilitates versioning of serialized data. Its value is stored with the data when serializing. When de-serializing, the same version is checked to see how the serialized data matches the current code.

serialVersionUID便于序列化数据的版本控制。它的值在序列化时与数据一起存储。反序列化时,会检查相同的版本以查看序列化数据如何与当前代码匹配。

If you want to version your data, you normally start with a serialVersionUIDof 0, and bump it with every structural change to your class which alters the serialized data (adding or removing non-transient fields).

如果要对数据进行版本控制,通常从serialVersionUID0 的a 开始,并在对类的每次结构更改时对其进行碰撞,这会改变序列化数据(添加或删除非瞬态字段)。

The built-in de-serialization mechanism (in.defaultReadObject()) will refuse to de-serialize from old versions of the data. But if you want to you can define your own readObject()-function which can read back old data. This custom code can then check the serialVersionUIDin order to know which version the data is in and decide how to de-serialize it. This versioning technique is useful if you store serialized data which survives several versions of your code.

内置的反序列化机制 ( in.defaultReadObject()) 将拒绝从旧版本的数据进行反序列化。但是,如果您愿意,您可以定义自己的readObject()函数,该函数可以读回旧数据。然后,此自定义代码可以检查serialVersionUID数据以了解数据所在的版本并决定如何对其进行反序列化。如果您存储在代码的多个版本中幸存下来的序列化数据,则此版本控制技术非常有用。

But storing serialized data for such a long time span is not very common. It is far more common to use the serialization mechanism to temporarily write data to for instance a cache or send it over the network to another program with the same version of the relevant parts of the codebase.

但是将序列化数据存储这么长的时间跨度并不常见。更常见的是使用序列化机制将数据临时写入例如缓存或通过网络将其发送到具有代码库相关部分的相同版本的另一个程序。

In this case you are not interested in maintaining backwards compatibility. You are only concerned with making sure that the code bases which are communicating indeed have the same versions of relevant classes. In order to facilitate such a check, you must maintain the serialVersionUIDjust like before and not forget to update it when making changes to your classes.

在这种情况下,您对保持向后兼容性不感兴趣。您只关心确保正在通信的代码库确实具有相同版本的相关类。为了便于这样的检查,您必须serialVersionUID像以前一样维护它,并且在更改类时不要忘记更新它。

If you do forget to update the field, you might end up with two different versions of a class with different structure but with the same serialVersionUID. If this happens, the default mechanism (in.defaultReadObject()) will not detect any difference, and try to de-serialize incompatible data. Now you might end up with a cryptic runtime error or silent failure (null fields). These types of errors might be hard to find.

如果您确实忘记更新该字段,则最终可能会得到具有不同结构但具有相同serialVersionUID. 如果发生这种情况,默认机制 ( in.defaultReadObject()) 将不会检测任何差异,并尝试反序列化不兼容的数据。现在您可能会遇到一个神秘的运行时错误或无提示失败(空字段)。这些类型的错误可能很难找到。

So to help this usecase, the Java platform offers you a choice of not setting the serialVersionUIDmanually. Instead, a hash of the class structure will be generated at compile-time and used as id. This mechanism will make sure that you never have different class structures with the same id, and so you will not get these hard-to-trace runtime serialization failures mentioned above.

所以为了帮助这个用例,Java 平台为您提供了一个不serialVersionUID手动设置的选择。相反,类结构的散列将在编译时生成并用作 id。这种机制将确保您永远不会有具有相同 id 的不同类结构,因此您不会遇到上面提到的这些难以跟踪的运行时序列化失败。

But there is a backside to the auto-generated id strategy. Namely that the generated ids for the same class might differ between compilers (as mentioned by Jon Skeet above). So if you communicate serialized data between code compiled with different compilers, it is recommended to maintain the ids manually anyway.

但是自动生成的 id 策略有一个背面。也就是说,同一类的生成 id 可能因编译器而异(如上面 Jon Skeet 所述)。因此,如果您在使用不同编译器编译的代码之间传递序列化数据,建议无论如何手动维护 id。

And if you are backwards-compatible with your data like in the first use case mentioned, you also probably want to maintain the id yourself. This in order to get readable ids and have greater control over when and how they change.

如果您像在提到的第一个用例中那样向后兼容您的数据,您可能还想自己维护 id。这是为了获得可读的 id 并更好地控制它们的更改时间和方式。

回答by Mukti

Field data represents some information stored in the class. Class implements the Serializableinterface, so eclipse automatically offered to declare the serialVersionUIDfield. Lets start with value 1 set there.

字段数据表示存储在类中的一些信息。类实现了Serializable接口,所以eclipse自动提供声明serialVersionUID字段。让我们从在那里设置的值 1 开始。

If you don't want that warning to come, use this:

如果您不希望出现该警告,请使用以下命令:

@SuppressWarnings("serial")