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

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

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


当前回答

不用担心,默认计算非常好,可以满足999999%的情况。如果你遇到问题,你可以——正如已经说过的——在需要时引入UID(这是极不可能的)

其他回答

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

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

什么时候重要?

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

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

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

要理解字段serialVersionUID的重要性,应该了解序列化/反序列化的工作原理。

当序列化一个Serializable类对象时,Java Runtime将一个序列版本号(称为serialVersionUID)与这个序列化对象相关联。在反序列化此序列化对象时,Java Runtime将序列化对象的serialVersionUID与类的serialVersion UID匹配。如果两者都相等,则只有它继续执行进一步的反序列化过程,否则抛出InvalidClassException。

因此,我们得出结论,要使序列化/反序列化过程成功,序列化对象的serialVersionUID必须与类的serialVersion UID等效。如果程序员在程序中显式指定serialVersionUID值,那么无论序列化和反序列化平台如何,相同的值都将与序列化对象和类相关联(例如,可以使用sun或MS JVM在类似windows的平台上进行序列化,而反序列化可以使用Zing JVM在不同的平台Linux上进行)。

但是,如果程序员未指定serialVersionUID,则在对任何对象进行Serialization\DeSerialization时,Java运行时会使用自己的算法来计算它。这种serialVersionUID计算算法因JRE而异。对象序列化的环境也可能使用一个JRE(例如:SUN JVM),而取消序列化的环境则使用LinuxJvm(zing)。在这种情况下,与序列化对象关联的serialVersionUID将不同于在取消序列化环境中计算的类的serialVersion UID。反过来,反序列化将不会成功。所以为了避免这种情况/问题,程序员必须始终指定Serializable类的serialVersionUID。

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

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

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

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

以及更多。

如果您在一个从未想过序列化的类上收到此警告,并且您没有声明自己实现了Serializable,这通常是因为您继承了一个实现Serializable的超类。通常情况下,最好委托给这样的对象,而不是使用继承。

所以

public class MyExample extends ArrayList<String> {

    public MyExample() {
        super();
    }
    ...
}

do

public class MyExample {
    private List<String> myList;

    public MyExample() {
         this.myList = new ArrayList<String>();
    }
    ...
}

并且在相关方法中调用myList.foo()而不是this.foo()(或super.foo(())

我经常看到有人在扩展JFrame之类的东西,而实际上他们只需要授权。(这也有助于在IDE中自动完成,因为JFrame有数百个方法,当您想在类中调用自定义方法时,不需要这些方法。)

警告(或serialVersionUID)不可避免的一种情况是,从AbstractAction(通常在匿名类中)扩展时,只添加actionPerformed方法。我认为在这种情况下不应该有警告(因为你通常无法可靠地序列化和反序列化这些匿名类,不管是在你的类的不同版本中),但我不确定编译器如何识别这一点。

什么是serialVersionUID,为什么要使用它?

SerialVersionUID是每个类的唯一标识符,JVM使用它来比较类的版本,以确保在序列化期间使用的相同类在反序列化期间加载。

指定一个可以提供更多控制,但如果不指定,JVM会生成一个。不同编译器生成的值可能不同。此外,有时您只是出于某种原因想要禁止旧序列化对象的反序列化[向后不兼容],在这种情况下,您只需更改serialVersionUID。

Serializable的javadocs表示:

默认的serialVersionUID计算对类高度敏感详细信息可能因编译器实现而异因此,在反序列化。

因此,您必须声明serialVersionUID,因为它给了我们更多的控制权。

这篇文章在这个话题上有一些好的观点。