当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当前回答
java.io.Serializable的文档可能与您将得到的解释一样好:
序列化运行时与每个可序列化类关联一个版本号,称为serialVersionUID,该版本号在反序列化期间用于验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。如果接收方为对象加载的类具有与对应发送方类不同的serialVersionUID,则反序列化将导致InvalidClassException。可序列化类可以通过声明一个名为serialVersionUID的字段来显式声明自己的serialVersionUID,该字段必须是静态的、final的和long类型:
ANY-ACCESS-MODIFIER静态最终长序列版本UID=42L;
如果可序列化类未显式声明serialVersionUID,则序列化运行时将根据该类的各个方面计算该类的默认serialVersionID值,如Java(TM)对象序列化规范中所述。但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认的serialVersionUID计算对可能因编译器实现而异的类细节高度敏感,因此在反序列化期间可能会导致意外的InvalidClassExceptions。因此,为了保证不同java编译器实现之间的serialVersionUID值一致,可序列化类必须声明显式的serialVersion UID值。还强烈建议显式serialVersionUID声明在可能的情况下使用私有修饰符,因为此类声明仅适用于立即声明的类-serialVersionUID字段作为继承成员不有用。
其他回答
如果CheckStyle能够验证实现Serializable的类上的serialVersionUID是否具有良好的值,即它与串行版本id生成器将生成的值相匹配,这将是一件好事。例如,如果您有一个包含大量可序列化DTO的项目,那么记住删除现有的serialVersionUID并重新生成它是一件痛苦的事,而目前验证这一点的唯一方法(据我所知)是为每个类重新生成并与旧类进行比较。这是非常痛苦的。
我不能错过这个机会,插上乔什·布洛克(Josh Bloch)的书《有效的Java》(第二版)。第10章是关于Java序列化的不可或缺的资源。
根据Josh的说法,自动生成的UID是基于类名、实现的接口以及所有公共和受保护的成员生成的。以任何方式更改任何这些都将更改serialVersionUID。因此,只有当您确定不会有多个版本的类被串行化(跨进程或稍后从存储中检索)时,才不需要使用它们。
如果您现在忽略它们,然后发现需要以某种方式更改类,但保持与旧版本类的兼容性,则可以使用JDK工具serialver在旧类上生成serialVersionUID,并在新类上显式设置。(根据您的更改,您可能还需要通过添加writeObject和readObject方法来实现自定义序列化-请参阅Serializable javadoc或上述第10章。)
首先我需要解释什么是序列化。
序列化允许将对象转换为流,以便通过网络发送该对象或将其保存到文件或保存到DB中以供字母使用。
序列化有一些规则。
只有当对象的类或其超类实现serializable接口时,对象才可串行化对象是可序列化的(它本身实现了可序列化接口),即使它的超类不是。但是,可序列化类层次结构中的第一个超类(未实现可序列化接口)必须具有无参数构造函数。如果违反了这一点,readObject()将在运行时生成java.io.InvalidClassException所有基元类型都是可序列化的。瞬态字段(带有瞬态修饰符)未序列化(即未保存或还原)。实现Serializable的类必须标记不支持序列化的类的临时字段(例如,文件流)。静态字段(带有静态修饰符)未序列化。
当Object被序列化时,Java Runtime将序列版本号(即serialVersionID)关联起来。
我们需要serialVersionID的地方:
在反序列化期间,验证发送方和接收方在序列化方面是否兼容。如果接收方使用不同的serialVersionID加载了类,则反序列化将以InvalidClassCastException结束。可序列化类可以通过声明一个名为serialVersionUID的字段来显式声明自己的serialVersionID,该字段必须是静态、final和long类型。
让我们举个例子来试试。
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;
public String getEmpName() {
return name;
}
public void setEmpName(String empname) {
this.empname = empname;
}
public byte getEmpAge() {
return empage;
}
public void setEmpAge(byte empage) {
this.empage = empage;
}
public String whoIsThis() {
return getEmpName() + " is " + getEmpAge() + "years old";
}
}
创建序列化对象
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setEmpName("Jagdish");
employee.setEmpAge((byte) 30);
FileOutputStream fout = new
FileOutputStream("/users/Jagdish.vala/employee.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(employee);
oos.close();
System.out.println("Process complete");
}
}
反序列化对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException, IOException {
Employee employee = new Employee();
FileInputStream fin = new FileInputStream("/users/Jagdish.vala/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}
注意:现在更改Employee类的serialVersionUID并保存:
private static final long serialVersionUID = 4L;
并执行Reader类。不执行Writer类,您将得到异常。
Exception in thread "main" java.io.InvalidClassException:
com.jagdish.vala.java.serialVersion.Employee; local class incompatible:
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
“serialVersionUID”是一个64位数字,用于在反序列化过程中唯一标识类。序列化对象时,类的serialVersionUID也会写入文件。每当反序列化此对象时,java运行时都会从序列化数据中提取此serialVersionUID值,并比较与该类关联的相同值。如果两者不匹配,则将抛出“java.io.InvalidClassException”。
如果可序列化类未显式声明serialVersionUID,则序列化运行时将基于类的各个方面(如字段、方法等)计算该类的serialVersionID值。您可以参考此链接获取演示应用程序。
我通常在一个上下文中使用serialVersionUID:当我知道它将离开JavaVM的上下文时。
当我为我的应用程序使用ObjectInputStream和ObjectOutputStream时,或者如果我知道我使用的库/框架将使用它时,我会知道这一点。serialVersionID确保不同版本或供应商的不同Java VM能够正确互操作,或者如果它在VM外部存储和检索(例如HttpSession),会话数据即使在应用服务器。
对于所有其他情况,我使用
@SuppressWarnings("serial")
因为大多数时候默认的serialVersionUID就足够了。这包括异常、HttpServlet。