当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当前回答
最初的问题是问“为什么它很重要”和“示例”,这个串行版本ID在哪里有用。我找到了一个。
假设您创建了一个Car类,将其实例化,并将其写入对象流。扁平的汽车对象在文件系统中放置一段时间。同时,如果通过添加新字段来修改Car类。稍后,当您尝试读取(即反序列化)扁平化的Car对象时,会得到java.io.InvalidClassException——因为所有可序列化的类都会自动给定一个唯一的标识符。当类的标识符不等于展平对象的标识符时,将引发此异常。如果你真的想一想,由于添加了新字段,就会引发异常。通过声明显式的serialVersionUID来控制版本控制,可以避免引发此异常。显式声明serialVersionUID(因为不必计算)也有一个小的性能优势。因此,最好在创建Serializable类后立即将自己的serialVersionUID添加到它们中,如下所示:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
其他回答
例如,缺少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
不用担心,默认计算非常好,可以满足999999%的情况。如果你遇到问题,你可以——正如已经说过的——在需要时引入UID(这是极不可能的)
如果您想修改大量最初没有设置serialVersionUID的类,同时保持与旧类的兼容性,IntelliJ Idea、Eclipse等工具会产生随机数,并且不能一次性处理一堆文件,因此会出现问题。我提出了以下bash脚本(很抱歉,Windows用户,请考虑购买Mac或转换为Linux),以轻松解决serialVersionUID问题:
base_dir=$(pwd)
src_dir=$base_dir/src/main/java
ic_api_cp=$base_dir/target/classes
while read f
do
clazz=${f//\//.}
clazz=${clazz/%.java/}
seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
perl -ni.bak -e "print $_; printf qq{%s\n}, q{ private $seruidstr} if /public class/" $src_dir/$f
done
保存此脚本时,将add_serialVersionUID.sh设置为~/bin。然后在Maven或Gradle项目的根目录中运行它,如下所示:
add_serialVersionUID.sh < myJavaToAmend.lst
此.lst包含用于以以下格式添加serialVersionUID的java文件列表:
com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java
该脚本使用了JDK serialVer工具。因此,请确保$JAVA_HOME/bin位于PATH中。
什么是serialVersionUID,为什么要使用它?
SerialVersionUID是每个类的唯一标识符,JVM使用它来比较类的版本,以确保在序列化期间使用的相同类在反序列化期间加载。
指定一个可以提供更多控制,但如果不指定,JVM会生成一个。不同编译器生成的值可能不同。此外,有时您只是出于某种原因想要禁止旧序列化对象的反序列化[向后不兼容],在这种情况下,您只需更改serialVersionUID。
Serializable的javadocs表示:
默认的serialVersionUID计算对类高度敏感详细信息可能因编译器实现而异因此,在反序列化。
因此,您必须声明serialVersionUID,因为它给了我们更多的控制权。
这篇文章在这个话题上有一些好的观点。
首先回答您的问题,当我们不在类中声明SerialVersionUID时,Java运行时会为我们生成它,但该过程对许多类元数据敏感,包括字段数、字段类型、字段的访问修饰符、类实现的接口等。因此,建议我们自己声明它,Eclipse也会警告您。
序列化:我们经常处理状态(对象变量中的数据)非常重要的重要对象,因此在将对象状态发送到其他机器时,我们不会因电源/系统故障(或网络故障)而丢失它。这个问题的解决方案被命名为“持久性”,这意味着持久化(保存/保存)数据。串行化是实现持久性的许多其他方法之一(通过将数据保存到磁盘/内存)。保存对象的状态时,为对象创建标识,以便能够正确地将其读回(反序列化),这一点非常重要。此唯一标识ID为SerialVersionUID。