为啥存在 serialVersionUID 字段?
Posted
技术标签:
【中文标题】为啥存在 serialVersionUID 字段?【英文标题】:Why does the serialVersionUID field exist?为什么存在 serialVersionUID 字段? 【发布时间】:2012-08-31 21:16:19 【问题描述】:Serializable
接口的推出让我感到困惑,为什么我必须在我的所有课程中加入这个字段。我知道这个接口需要一个唯一的标识符来标记类,但为什么他们不能在运行时生成它。例如,他们可以使用完全限定类名的 MD5 散列或用于在罕见情况下处理重复项的类似方法来生成它(我敢肯定,当被要求生成 id 时,eclipse 会做什么)。
所以我要问的是(不,这篇文章不仅仅是对标准库的咆哮)框架到底是如何使用序列化字段的?
我想知道的原因是因为我将尝试创建一个 Aspect(在 AspectJ 或其他语言中),它将使用 MD5 哈希添加 serialVersionUID 字段,并且能够以可接受的方式处理冲突API。
如果我能得到它的工作,我会发布结果。
【问题讨论】:
“Serializable 接口的推出让我感到困惑” 由“启动”DYM Java 1.1?那是一个很长的时间.. 【参考方案1】:不需要serialVersionUID
字段。如果您不提供,Java 将根据您的类的字段和方法生成一个。
您可能要指定serialVersionUID
的原因是为了防止在方法更改时值发生更改,这不会影响序列化的二进制文件。考虑类:
public class Person implements Serializable
private String name;
public String getName()
return name;
未指定serialVersionUID
。如果你运行 serialver Person
它会返回:
Person: static final long serialVersionUID = 3793453319058452486L;
现在您决定添加一个方法,但保持字段不变。
public class Person implements Serializable
private String name;
public String getName()
return name;
public Object foo()
return "bar";
序列化后的二进制仍然与旧版本完全兼容,但serialVersionUID
不同:
Person: static final long serialVersionUID = -6188734029437600310L;
使用不同的serialVersionUID
,反序列化将导致 serialVersionUID 不匹配错误。解决方法是声明您自己的serialVersionUID
,将其设置为任何值(我将其设置为1L
),并在字段发生更改时更改它。
另请参阅related question "What is a serialVersionUID and why should I use it?" 以获得更详细的讨论。
【讨论】:
您可能会强调该值不需要在任何形状或形式上都是唯一的。【参考方案2】:为什么我必须在我的所有课程中加入这个字段
你没有。
为什么他们不能在运行时生成这个
除非你自己提供,否则他们会这样做。
我将尝试创建一个 Aspect(在 AspectJ 或其他语言中),它将使用 MD5 哈希添加 serialVersionUID 字段
那将毫无意义。你不明白它是干什么用的。在运行时生成它是默认情况下已经发生的,因此您的提案没有增加任何价值。该值在编译时指定它,以便吸收实际上不会破坏对象版本控制兼容性的次要类更改。您无法通过 AOP 来实现这一点。这是一个编码决定。
【讨论】:
【参考方案3】:
Serializable
接口
此接口存在于java.io
包中。这是为了保护较小的更改,并允许用户在运行时write
和save
对象。这个接口由所有的swing组件实现,因为开发者可以扩展它们,以便JVM可以找到组件的新版本,即Your class
。
SerialVersionUID
serialVersionUID
在 Serializable 类中用作版本控制。如果您没有根据您的 Serializable 类的各个方面显式声明一个 serialVersionUID JVM will did it for you automatically
,如Java(TM) Object Serialization Specification
中所述
【讨论】:
【参考方案4】:正如@Steve Kuo 所说,不需要serialVersionUID
字段。
令人沮丧,因为它没有作为对象可序列化合同的一部分强制执行,我们团队中大约一半的开发人员这样做,而另一半则没有。大多数这样做的人通常只是设置private static final long serialVersionUID = 1L;
(尽管一些开发人员喜欢利用它作为尝试提出伪随机 Longs 的机会......)
话虽如此,我一直认为这是对可序列化对象进行版本控制的初步尝试。
假设我们有:
public class PersonDTO implements Serializable
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
// Appropriate getters/setters of course
后来我们要添加一个新字段private String middleInitial
。
如果我们将 serialVersionUID 增加到 2,我们可以使用它来指示类已更改,并且旧的已序列化具有 serialVersionUID 的 PersonDTO 实例无法使用此修改后的类定义反序列化。
【讨论】:
人们可以为 not 指定serialVersionUID
提供理由,因为人们在更改字段时不会更改值。我通常不指定serialVersionUID
,除非我的应用程序将使用不同版本的序列化二进制文件运行。您始终可以使用serialver
确定原始自动生成的serialVersionUID
值。我在 Eclipse 中禁用了缺少的 serialVersionUID
警告。
这里有几个误解。添加字段不会破坏序列化兼容性。所以实际上你不应该在这种情况下更改serialVersionUID
值:你所做的只是导致否则不兼容。使用值 1 或 -1 没有任何问题,只要它从类生命的开始就存在。
这是一个很好的观点。正如我所说,这令人沮丧,因为我们有不同的开发人员对其采取不同的策略。我个人也不会依赖它或主张将其用作版本控制机制,我只是试图解释我对可能使用的理解。
好的,所以使用相同的serialVersionUID
添加字段是安全的。但是,如果我记得使用相同的 serialVersionUID
更改或删除字段是不安全的。
@SteveKuo 添加、删除和重新排序字段都是安全的。更改它们的类型或类继承链或其名称或包都不是。这一切都记录在Object Versioning chapter of the Object Serialization Specification中。以上是关于为啥存在 serialVersionUID 字段?的主要内容,如果未能解决你的问题,请参考以下文章
为啥java中的serialVersionUID必须是静态的、最终的和long类型的? [复制]
这是啥意思:可序列化的类没有声明一个静态的最终 serialVersionUID 字段? [复制]
Applet(不声明 long 类型的静态最终 serialVersionUID 字段)