当类在Eclipse中实现Serializable时,我有两个选项:添加默认的serialVersionUID(1L)或生成的serialVersionUID(3567653491060394677L)。我认为第一个选项更酷,但很多次我看到人们使用第二个选项。是否有理由生成较长的serialVersionUID?


据我所知,这只是为了与以前的版本兼容。只有当您之前忽略了使用serialVersionUID,然后进行了您知道应该是兼容的但导致序列化中断的更改时,这才有用。

有关更多细节,请参阅Java Serialization Spec。


序列化版本UID的目的是跟踪类的不同版本,以便执行有效的对象序列化。

其思想是生成一个对类的特定版本唯一的ID,然后在向类添加新细节(例如新字段)时更改该ID,这将影响序列化对象的结构。

总是使用相同的ID(例如1L)意味着在将来,如果类定义发生更改,导致序列化对象的结构发生更改,那么在尝试反序列化对象时很可能会出现问题。

如果省略了ID, Java将根据对象的字段实际为您计算ID,但我认为这是一个开销很大的过程,因此手动提供一个将提高性能。

下面是一些讨论类的序列化和版本控制的文章的链接:

JDC技术提示:2000年2月29日(链接在2013年2月中断) 了解Java Serialization API的秘密


生成一个类的主要原因是使它与已经有持久化副本的类的现有版本兼容。


如果您没有指定serialVersionUID,那么Java会动态生成一个。生成的serialVersionUID就是这个数字。如果您更改了类中的某些内容,而这些内容并没有真正使您的类与以前的序列化版本不兼容,而是更改了散列,那么您需要使用生成的非常大的数字serialVersionUID(或错误消息中的“预期”数字)。否则,如果你自己记录所有东西,0,1,2…是更好的。


serialVersionUID的“long”默认值是Java序列化规范定义的默认值,由默认序列化行为计算得出。

因此,如果您添加了默认版本号,只要在结构上没有任何变化,您的类就会(反)序列化得更快,但是您必须注意,如果您更改了类(添加/删除字段),那么您也会更新序列号。

如果你不需要兼容现有的比特流,你可以把1L放在那里,然后在有变化时根据需要增加版本。也就是说,当更改后的类的默认序列化版本与旧类的默认版本不同时。


每次定义时,绝对应该创建一个serialVersionUID 实现java.io.Serializable的类。如果你不这样做,别人会的 自动为你创建,但这很糟糕。自动生成 serialVersionUID基于类的方法签名,因此 如果您在将来更改类以添加方法(例如), 反序列化类的“旧”版本将失败。这就是 可能发生:

创建类的第一个版本,而不定义 serialVersionUID。 将类的实例序列化到持久存储中;一个 serialVersionUID为您自动生成。 修改类以添加新方法,并重新部署应用程序。 尝试反序列化在第2步中序列化的实例,但现在它失败了(当它应该成功时),因为它有 不同的自动生成serialVersionUID。


因为在许多情况下,默认id不是唯一的。所以我们创建id是为了创造独特的概念。


Well, serialVersionUID is an exception to the rule that “static fields don’t get serialized”. ObjectOutputStream writes every time the value of serialVersionUID to the output stream. ObjectInputStream reads it back and if the value read from the stream does not agree with the serialVersionUID value in the current version of the class, then it throws the InvalidClassException. Moreover, if there is no serialVersionUID officially declared in the class to be serialized, compiler automatically adds it with a value generated based on the fields declared in the class.


当您使用serialVersionUID(1L)而不是生成serialVersionUID(3567653491060394677L)时,您是在说一些事情。

您是在说,您100%确信没有系统会接触到这个类,而这个类的版本号为1的序列化版本是不兼容的。

如果您能想出任何理由来说明它的序列化版本历史是未知的,那可能很难有信心地说出来。在它的生命周期中,一个成功的类将由许多人维护,存在于许多项目中,并驻留在许多系统中。

你可以为此苦恼。或者你可以买彩票,希望输。如果您生成了这个版本,那么就有很小的出错几率。如果你假设“嘿,我敢打赌还没有人使用1”,那么你的几率就很大。正是因为我们都认为0和1很酷,所以你击中它们的几率更高。

-

当您生成serialVersionUID(3567653491060394677L)而不是使用serialVersionUID(1L)时,您是在说一些事情。

你是说人们可能在这个类的历史上手动创建或生成了其他版本号,你不在乎,因为long是非常大的数字。

无论哪种方式,除非您完全了解在序列化该类时所使用的版本号的历史,以及该类已经存在或将存在的整个宇宙,否则您都是在冒险。如果你有时间100%确定1是ok的,那就试试吧。如果工作量太大,那就盲目地生成数字。你中彩票的可能性比出错的可能性更大。如果有,告诉我,我请你喝杯啤酒。

说了这么多玩彩票的内容,我可能给您的印象是serialVersionUID是随机生成的。事实上,只要数字的范围均匀分布在long的每个可能值上,就可以了。然而,它实际上是这样做的:

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100

唯一的区别是你不需要随机来源。您正在使用类本身的更改来更改结果。但根据鸽子洞原理,它仍然有可能出错并发生碰撞。这简直太不可能了。祝你能从我嘴里套出啤酒来。

然而,即使类永远只存在于一个系统和一个代码库中,认为手动增加数量会导致零碰撞的可能性只意味着您不了解人类。:)


为了补充@David Schmitts的回答,作为经验法则,我总是使用默认的1L。我只需要返回并更改其中一些数几次,但当我进行更改并每次更新默认数时,我知道这一点。

在我目前的公司,他们要求自动生成的号码,所以我使用它作为约定,但我更喜欢默认的。我的观点是,如果这不是您工作的约定,请使用默认值,除非您认为由于某种原因将不断更改序列化类的结构。