当类在 Eclipse 中实现 Serializable 时,我有两个选择:添加默认值serialVersionUID(1L)
或生成serialVersionUID(3567653491060394677L)
。我认为第一个更酷,但很多时候我看到人们使用第二个选项。有什么理由生成long serialVersionUID
吗?
11 回答
据我所知,这只是为了与以前的版本兼容。这仅在您之前忽略使用 serialVersionUID,然后进行了您知道应该兼容但导致序列化中断的更改时才有用。
有关更多详细信息,请参阅Java 序列化规范。
序列化版本 UID 的目的是跟踪类的不同版本,以便执行对象的有效序列化。
这个想法是生成一个特定版本的类的唯一ID,然后当有新的细节添加到类时更改,例如新的字段,这会影响序列化对象的结构。
始终使用相同的 ID,例如1L
意味着将来,如果更改类定义导致序列化对象的结构发生变化,那么在尝试反序列化对象时很可能会出现问题。
如果省略 ID,Java 实际上会根据对象的字段为您计算 ID,但我相信这是一个昂贵的过程,因此手动提供一个会提高性能。
以下是一些讨论类的序列化和版本控制的文章的链接:
- JDC 技术提示:2000 年 2 月 29 日 (链接已于 2013 年 2 月断开)
- 发现 Java 序列化 API 的秘密
生成的主要原因是使其与已经具有持久副本的类的现有版本兼容。
的“长”默认值是Java 序列化规范serialVersionUID
定义的默认值,根据默认序列化行为计算得出。
因此,如果您添加默认版本号,只要结构上没有任何变化,您的类将(反)序列化更快,但您必须注意,如果您更改类(添加/删除字段),您也会更新序列号。
如果您不必与现有的比特流兼容,您可以放在1L
那里并在发生变化时根据需要增加版本。也就是说,当更改类的默认序列化版本与旧类的默认版本不同时。
每次定义实现的类时,您绝对应该创建一个 serialVersionUID java.io.Serializable
。如果您不这样做,则会自动为您创建一个,但这很糟糕。自动生成的 serialVersionUID 基于类的方法签名,因此如果您将来更改类以添加方法(例如),反序列化类的“旧”版本将失败。以下是可能发生的情况:
- 创建类的第一个版本,而不定义 serialVersionUID。
- 将您的类的实例序列化为持久存储;会自动为您生成一个 serialVersionUID。
- 修改您的类以添加新方法,然后重新部署您的应用程序。
- 尝试反序列化在步骤 2 中序列化的实例,但现在它失败了(当它应该成功时),因为它具有不同的自动生成的 serialVersionUID。
如果您不指定 serialVersionUID,那么 Java 会即时生成一个。生成的 serialVersionUID 就是那个数字。如果您更改类中的某些内容并不会真正使您的类与以前的序列化版本不兼容,而是更改了哈希,那么您需要使用生成的超大数 serialVersionUID(或错误消息中的“预期”数字) . 否则,如果您自己跟踪所有内容,则 0、1、2... 会更好。
当您使用 serialVersionUID(1L) 而不是生成 serialVersionUID(3567653491060394677L) 时,您是在说些什么。
你是说你有 100% 的信心,没有一个系统会接触这个类,它的版本号为 1 的这个类的序列化版本不兼容。
如果你能想到任何借口让它的序列化版本历史不为人知,那可能很难有把握地说。在它的一生中,一个成功的类将由许多人维护,存在于许多项目中,并存在于许多系统中。
你可以为此苦恼。或者你可以玩彩票希望输。如果您生成该版本,则出现问题的可能性很小。如果您假设“嘿,我打赌还没有人用过 1”,那么您的赔率就非常大了。正是因为我们都认为 0 和 1 很酷,所以你击中它们的几率更高。
-
当您生成 serialVersionUID(3567653491060394677L) 而不是使用 serialVersionUID(1L) 时,您是在说些什么。
你是说人们可能在这个类的历史上手动创建或生成了其他版本号,你不在乎,因为 Longs 的数字很大。
无论哪种方式,除非您完全了解在该类已经存在或将要存在的整个宇宙中序列化该类时使用的版本号历史,否则您就是在冒险。如果您有时间 100% 确定 1 是 AOK,那就去做吧。如果工作量太大,请继续盲目地生成数字。你更有可能赢得彩票而不是出错。如果是的话,告诉我,我给你买啤酒。
通过所有这些关于玩彩票的讨论,我可能给你的印象是serialVersionUID 是随机生成的。事实上,只要数字范围均匀分布在 Long 的每个可能值上就可以了。然而,它实际上是这样完成的:
http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html#4100
你得到的唯一区别是你不需要随机源。您正在使用类本身的更改来更改结果。但是根据鸽巢原理,它仍然有可能出错并发生碰撞。这是难以置信的可能性。祝我好运。
然而,即使该类只存在于一个系统和一个代码库中,认为手动增加数字会使您发生冲突的可能性为零,但这只是意味着您不了解人类。:)
好吧,serialVersionUID 是“静态字段不被序列化”规则的一个例外。ObjectOutputStream 每次将serialVersionUID 的值写入输出流。ObjectInputStream 将其读回,如果从流中读取的值与当前版本的类中的 serialVersionUID 值不一致,则抛出 InvalidClassException。而且,如果要序列化的类中没有官方声明的serialVersionUID,编译器会自动添加一个根据类中声明的字段生成的值。
因为在很多情况下,默认 id 不是唯一的。所以我们创建 id 来制作独特的概念。
要添加到@David Schmitts 的答案,根据经验,我总是会不习惯使用默认的 1L。我只需要返回并更改其中的一些几次,但我知道当我进行更改并每次将默认数字更新为 1 时。
在我目前的公司,他们需要自动生成的号码,所以我将其用于约定,但我更喜欢默认值。我的看法是,如果这不是您工作的约定,请使用默认值,除非您认为出于某种原因会不断更改序列化类的结构。
序列化版本 UID 的目的是跟踪类的不同版本,以便执行对象的有效序列化。
这个想法是生成一个特定版本的类的唯一ID,然后当有新的细节添加到类时更改,例如新的字段,这会影响序列化对象的结构。
一个简单的解释:
你在序列化数据吗?
序列化基本上是将类数据写入文件/流/等。反序列化是将数据读回类。
你打算投入生产吗?
如果您只是用不重要/假数据测试某些东西,那么不要担心(除非您直接测试序列化)。
这是第一个版本吗?
如果是这样,设置 serialVersionUID=1L。
这是第二个、第三个等产品版本吗?
现在您需要担心 serialVersionUID,并且应该深入研究它。
基本上,如果在更新需要写入/读取的类时没有正确更新版本,则在尝试读取旧数据时会出错。