当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当前回答
每次序列化对象时,对象都会标记对象类的版本ID号。此ID称为serialVersionUID,它是根据有关类结构的信息计算的。假设您创建了一个Employee类,它的版本id为#333(由JVM分配),现在当您将序列化该类的对象(假设Employees对象)时,JVM将为其分配UID为#333。
考虑一种情况——将来您需要编辑或更改类,在这种情况下,当您修改它时,JVM将为它分配一个新的UID(假设#444)。现在,当您尝试反序列化雇员对象时,JVM会将序列化对象(雇员对象)的版本ID(#333)与类的版本ID进行比较,即#444(自更改以来)。相比之下,JVM将发现两个版本UID不同,因此反序列化将失败。因此,如果每个类的serialVersionID由程序员自己定义。即使类在未来演变,它也将是相同的,因此JVM将始终发现类与序列化对象兼容,即使类已更改。有关更多信息,请参阅HEAD FIRST JAVA的第14章。
其他回答
最初的问题是问“为什么它很重要”和“示例”,这个串行版本ID在哪里有用。我找到了一个。
假设您创建了一个Car类,将其实例化,并将其写入对象流。扁平的汽车对象在文件系统中放置一段时间。同时,如果通过添加新字段来修改Car类。稍后,当您尝试读取(即反序列化)扁平化的Car对象时,会得到java.io.InvalidClassException——因为所有可序列化的类都会自动给定一个唯一的标识符。当类的标识符不等于展平对象的标识符时,将引发此异常。如果你真的想一想,由于添加了新字段,就会引发异常。通过声明显式的serialVersionUID来控制版本控制,可以避免引发此异常。显式声明serialVersionUID(因为不必计算)也有一个小的性能优势。因此,最好在创建Serializable类后立即将自己的serialVersionUID添加到它们中,如下所示:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
长话短说,此字段用于检查序列化数据是否可以正确反序列化。序列化和反序列化通常由程序的不同副本进行,例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。此字段告诉两者对该对象的概念相同。此字段在以下情况下有帮助:
你在不同的地方有很多不同的程序副本(比如1个服务器和100个客户端)。如果您将更改对象、更改版本号并忘记更新此客户端,它将知道他无法反序列化您已将数据存储在某个文件中,稍后您尝试使用带有修改对象的程序的更新版本打开它-如果您保持正确的版本,您将知道此文件不兼容
什么时候重要?
最明显的是,如果向对象中添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。
不太明显——反序列化对象时,字符串中不存在的字段将保持为NULL。如果您已经从对象中删除了字段,则旧版本会将此字段始终保持为NULL,如果旧版本依赖此字段中的数据,则会导致错误行为(无论如何,您创建它是为了某种目的,而不仅仅是为了好玩:-))
最不明显的-有时你改变了你在某个领域的含义。例如,当你12岁时,你的意思是“自行车”下的“自行车”,但当你18岁时,意思是“摩托车”-如果你的朋友邀请你“骑自行车穿越城市”,而你将是唯一一个骑自行车来的人,你将不明白在不同领域保持相同的含义是多么重要:-)
什么是serialVersionUID,为什么要使用它?
SerialVersionUID是每个类的唯一标识符,JVM使用它来比较类的版本,以确保在序列化期间使用的相同类在反序列化期间加载。
指定一个可以提供更多控制,但如果不指定,JVM会生成一个。不同编译器生成的值可能不同。此外,有时您只是出于某种原因想要禁止旧序列化对象的反序列化[向后不兼容],在这种情况下,您只需更改serialVersionUID。
Serializable的javadocs表示:
默认的serialVersionUID计算对类高度敏感详细信息可能因编译器实现而异因此,在反序列化。
因此,您必须声明serialVersionUID,因为它给了我们更多的控制权。
这篇文章在这个话题上有一些好的观点。
我不能错过这个机会,插上乔什·布洛克(Josh Bloch)的书《有效的Java》(第二版)。第10章是关于Java序列化的不可或缺的资源。
根据Josh的说法,自动生成的UID是基于类名、实现的接口以及所有公共和受保护的成员生成的。以任何方式更改任何这些都将更改serialVersionUID。因此,只有当您确定不会有多个版本的类被串行化(跨进程或稍后从存储中检索)时,才不需要使用它们。
如果您现在忽略它们,然后发现需要以某种方式更改类,但保持与旧版本类的兼容性,则可以使用JDK工具serialver在旧类上生成serialVersionUID,并在新类上显式设置。(根据您的更改,您可能还需要通过添加writeObject和readObject方法来实现自定义序列化-请参阅Serializable javadoc或上述第10章。)
如果您序列化只是因为为了实现而必须序列化(例如,谁会关心您是否为HTTPSession序列化……如果它是否存储,您可能不关心对表单对象进行反序列化),那么您可以忽略这一点。
如果您实际使用的是序列化,那么只有计划直接使用序列化存储和检索对象时才重要。serialVersionUID表示类的版本,如果类的当前版本与其前一版本不向后兼容,则应递增该版本。
大多数时候,您可能不会直接使用序列化。如果是这种情况,请通过单击快速修复选项生成默认的SerialVersionUID,不用担心。