当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当缺少serialVersionUID时,Eclipse会发出警告。
可序列化类Foo未声明静态finallong类型的serialVersionUID字段
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
当前回答
简单解释:
您正在序列化数据吗?序列化基本上是将类数据写入文件/流等。反序列化是将数据读回类。你打算投入生产吗?如果您只是用不重要的/假的数据测试一些东西,那么不要担心它(除非您是直接测试序列化)。这是第一个版本吗?如果是,请将serialVersionUID设置为1L。这是第二、第三等prod版本吗?现在您需要担心serialVersionUID,并且应该深入研究它。
基本上,如果在更新需要写入/读取的类时没有正确更新版本,那么在尝试读取旧数据时会出现错误。
其他回答
“serialVersionUID”是一个64位数字,用于在反序列化过程中唯一标识类。序列化对象时,类的serialVersionUID也会写入文件。每当反序列化此对象时,java运行时都会从序列化数据中提取此serialVersionUID值,并比较与该类关联的相同值。如果两者不匹配,则将抛出“java.io.InvalidClassException”。
如果可序列化类未显式声明serialVersionUID,则序列化运行时将基于类的各个方面(如字段、方法等)计算该类的serialVersionID值。您可以参考此链接获取演示应用程序。
首先我需要解释什么是序列化。
序列化允许将对象转换为流,以便通过网络发送该对象或将其保存到文件或保存到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)
长话短说,此字段用于检查序列化数据是否可以正确反序列化。序列化和反序列化通常由程序的不同副本进行,例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。此字段告诉两者对该对象的概念相同。此字段在以下情况下有帮助:
你在不同的地方有很多不同的程序副本(比如1个服务器和100个客户端)。如果您将更改对象、更改版本号并忘记更新此客户端,它将知道他无法反序列化您已将数据存储在某个文件中,稍后您尝试使用带有修改对象的程序的更新版本打开它-如果您保持正确的版本,您将知道此文件不兼容
什么时候重要?
最明显的是,如果向对象中添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。
不太明显——反序列化对象时,字符串中不存在的字段将保持为NULL。如果您已经从对象中删除了字段,则旧版本会将此字段始终保持为NULL,如果旧版本依赖此字段中的数据,则会导致错误行为(无论如何,您创建它是为了某种目的,而不仅仅是为了好玩:-))
最不明显的-有时你改变了你在某个领域的含义。例如,当你12岁时,你的意思是“自行车”下的“自行车”,但当你18岁时,意思是“摩托车”-如果你的朋友邀请你“骑自行车穿越城市”,而你将是唯一一个骑自行车来的人,你将不明白在不同领域保持相同的含义是多么重要:-)
不用担心,默认计算非常好,可以满足999999%的情况。如果你遇到问题,你可以——正如已经说过的——在需要时引入UID(这是极不可能的)
如果您序列化只是因为为了实现而必须序列化(例如,谁会关心您是否为HTTPSession序列化……如果它是否存储,您可能不关心对表单对象进行反序列化),那么您可以忽略这一点。
如果您实际使用的是序列化,那么只有计划直接使用序列化存储和检索对象时才重要。serialVersionUID表示类的版本,如果类的当前版本与其前一版本不向后兼容,则应递增该版本。
大多数时候,您可能不会直接使用序列化。如果是这种情况,请通过单击快速修复选项生成默认的SerialVersionUID,不用担心。