为啥存在 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 包中。这是为了保护较小的更改,并允许用户在运行时writesave 对象。这个接口由所有的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?

为啥java中的serialVersionUID必须是静态的、最终的和long类型的? [复制]

这是啥意思:可序列化的类没有声明一个静态的最终 serialVersionUID 字段? [复制]

Applet(不声明 long 类型的静态最终 serialVersionUID 字段)

Applet(未声明类型为long的静态最终serialVersionUID字段)

为什么阿里巴巴要求程序员谨慎修改serialVersionUID 字段的值