当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当前回答
如果您在一个从未想过序列化的类上收到此警告,并且您没有声明自己实现了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可能会导致问题:
我正在研究这个JavaEE应用程序,它由一个使用EJB模块的Web模块组成。web模块远程调用EJB模块,并传递实现Serializable的POJO作为参数。
这个POJO的类被打包在EJB jar中,并被打包在WEB模块的WEB-INF/lib中它自己的jar中。它们实际上是同一个类,但当我打包EJB模块时,我打开了这个POJO的jar,将其与EJB模块打包在一起。
对EJB的调用失败,出现以下异常,因为我没有声明其serialVersionUID:
Caused by: java.io.IOException: Mismatched serialization UIDs : Source
(Rep.
IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
= 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
= 6227F23FA74A9A52
如果CheckStyle能够验证实现Serializable的类上的serialVersionUID是否具有良好的值,即它与串行版本id生成器将生成的值相匹配,这将是一件好事。例如,如果您有一个包含大量可序列化DTO的项目,那么记住删除现有的serialVersionUID并重新生成它是一件痛苦的事,而目前验证这一点的唯一方法(据我所知)是为每个类重新生成并与旧类进行比较。这是非常痛苦的。
长话短说,此字段用于检查序列化数据是否可以正确反序列化。序列化和反序列化通常由程序的不同副本进行,例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。此字段告诉两者对该对象的概念相同。此字段在以下情况下有帮助:
你在不同的地方有很多不同的程序副本(比如1个服务器和100个客户端)。如果您将更改对象、更改版本号并忘记更新此客户端,它将知道他无法反序列化您已将数据存储在某个文件中,稍后您尝试使用带有修改对象的程序的更新版本打开它-如果您保持正确的版本,您将知道此文件不兼容
什么时候重要?
最明显的是,如果向对象中添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。
不太明显——反序列化对象时,字符串中不存在的字段将保持为NULL。如果您已经从对象中删除了字段,则旧版本会将此字段始终保持为NULL,如果旧版本依赖此字段中的数据,则会导致错误行为(无论如何,您创建它是为了某种目的,而不仅仅是为了好玩:-))
最不明显的-有时你改变了你在某个领域的含义。例如,当你12岁时,你的意思是“自行车”下的“自行车”,但当你18岁时,意思是“摩托车”-如果你的朋友邀请你“骑自行车穿越城市”,而你将是唯一一个骑自行车来的人,你将不明白在不同领域保持相同的含义是多么重要:-)
最初的问题是问“为什么它很重要”和“示例”,这个串行版本ID在哪里有用。我找到了一个。
假设您创建了一个Car类,将其实例化,并将其写入对象流。扁平的汽车对象在文件系统中放置一段时间。同时,如果通过添加新字段来修改Car类。稍后,当您尝试读取(即反序列化)扁平化的Car对象时,会得到java.io.InvalidClassException——因为所有可序列化的类都会自动给定一个唯一的标识符。当类的标识符不等于展平对象的标识符时,将引发此异常。如果你真的想一想,由于添加了新字段,就会引发异常。通过声明显式的serialVersionUID来控制版本控制,可以避免引发此异常。显式声明serialVersionUID(因为不必计算)也有一个小的性能优势。因此,最好在创建Serializable类后立即将自己的serialVersionUID添加到它们中,如下所示:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
每次序列化对象时,对象都会标记对象类的版本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章。