当缺少serialVersionUID时,Eclipse会发出警告。

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

什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。


当前回答

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

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

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

潜在的编程问题:可能的意外布尔赋值潜在编程问题:空指针访问不必要的代码:从不读取局部变量不必要的代码:冗余空检查不必要的代码:不必要的强制转换或“instanceof”

以及更多。

其他回答

SerialVersionUID用于对象的版本控制。也可以在类文件中指定serialVersionUID。不指定serialVersionUID的结果是,当您添加或修改类中的任何字段时,已经序列化的类将无法恢复,因为为新类和旧序列化对象生成的serialVersionID将不同。Java序列化过程依赖于正确的serialVersionUID来恢复序列化对象的状态,并在serialVersionID不匹配的情况下抛出Java.io.InvalidClassException

阅读更多信息:http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ

最初的问题是问“为什么它很重要”和“示例”,这个串行版本ID在哪里有用。我找到了一个。

假设您创建了一个Car类,将其实例化,并将其写入对象流。扁平的汽车对象在文件系统中放置一段时间。同时,如果通过添加新字段来修改Car类。稍后,当您尝试读取(即反序列化)扁平化的Car对象时,会得到java.io.InvalidClassException——因为所有可序列化的类都会自动给定一个唯一的标识符。当类的标识符不等于展平对象的标识符时,将引发此异常。如果你真的想一想,由于添加了新字段,就会引发异常。通过声明显式的serialVersionUID来控制版本控制,可以避免引发此异常。显式声明serialVersionUID(因为不必计算)也有一个小的性能优势。因此,最好在创建Serializable类后立即将自己的serialVersionUID添加到它们中,如下所示:

public class Car {
    static final long serialVersionUID = 1L; //assign a long value
}

长话短说,此字段用于检查序列化数据是否可以正确反序列化。序列化和反序列化通常由程序的不同副本进行,例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。此字段告诉两者对该对象的概念相同。此字段在以下情况下有帮助:

你在不同的地方有很多不同的程序副本(比如1个服务器和100个客户端)。如果您将更改对象、更改版本号并忘记更新此客户端,它将知道他无法反序列化您已将数据存储在某个文件中,稍后您尝试使用带有修改对象的程序的更新版本打开它-如果您保持正确的版本,您将知道此文件不兼容

什么时候重要?

最明显的是,如果向对象中添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。

不太明显——反序列化对象时,字符串中不存在的字段将保持为NULL。如果您已经从对象中删除了字段,则旧版本会将此字段始终保持为NULL,如果旧版本依赖此字段中的数据,则会导致错误行为(无论如何,您创建它是为了某种目的,而不仅仅是为了好玩:-))

最不明显的-有时你改变了你在某个领域的含义。例如,当你12岁时,你的意思是“自行车”下的“自行车”,但当你18岁时,意思是“摩托车”-如果你的朋友邀请你“骑自行车穿越城市”,而你将是唯一一个骑自行车来的人,你将不明白在不同领域保持相同的含义是多么重要:-)

serialVersionUID有助于序列化数据的版本控制。序列化时,其值与数据一起存储。反序列化时,将检查同一版本,以查看序列化数据与当前代码的匹配情况。

如果要对数据进行版本化,通常从serialVersionUID 0开始,并将其与更改序列化数据(添加或删除非瞬时字段)的类的每一个结构更改一起转储。

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

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

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

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

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

但自动生成id策略也有其背后的原因。也就是说,为同一类生成的id在编译器之间可能会有所不同(正如Jon Skeet所提到的)。因此,如果在使用不同编译器编译的代码之间传递序列化数据,建议无论如何都手动维护id。

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

我通常在一个上下文中使用serialVersionUID:当我知道它将离开JavaVM的上下文时。

当我为我的应用程序使用ObjectInputStream和ObjectOutputStream时,或者如果我知道我使用的库/框架将使用它时,我会知道这一点。serialVersionID确保不同版本或供应商的不同Java VM能够正确互操作,或者如果它在VM外部存储和检索(例如HttpSession),会话数据即使在应用服务器。

对于所有其他情况,我使用

@SuppressWarnings("serial")

因为大多数时候默认的serialVersionUID就足够了。这包括异常、HttpServlet。