What is a serialVersionUID and why should I use it?

What is a serialVersionUID and why should I use it?

技术背景

在Java编程中,序列化(Serialization)是一种将对象转换为字节流的机制,方便对象的存储和传输;反序列化(Deserialization)则是将字节流恢复为对象的过程。在序列化和反序列化的过程中,serialVersionUID 起着至关重要的作用。Eclipse等开发工具在检测到可序列化类缺少 serialVersionUID 时会发出警告,这提示开发者需要关注这个重要的字段。

实现步骤

1. 理解 serialVersionUID 的作用

serialVersionUID 是一个唯一标识符,用于在反序列化时验证发送方和接收方的类版本是否兼容。如果接收方加载的类的 serialVersionUID 与发送方的类不同,反序列化将抛出 InvalidClassException

2. 显式声明 serialVersionUID

可序列化类可以通过声明一个名为 serialVersionUID 的静态、最终且类型为 long 的字段来显式指定其值。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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 empname;
}

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";
}
}

3. 序列化对象

使用 ObjectOutputStream 将对象写入文件或流。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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");
}
}

4. 反序列化对象

使用 ObjectInputStream 从文件或流中读取对象。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Reader {
public static void main(String[] args) throws ClassNotFoundException, IOException {
FileInputStream fin = new FileInputStream("/users/Jagdish.vala/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
Employee employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}

5. 验证 serialVersionUID 的重要性

更改 Employee 类的 serialVersionUID,再次运行 Reader 类,会抛出 InvalidClassException。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.Serializable;

public class Employee implements Serializable {
private static final long serialVersionUID = 4L; // 修改版本号
private String empname;
private byte empage;

public String getEmpName() {
return empname;
}

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";
}
}

核心代码

显式声明 serialVersionUID

1
private static final long serialVersionUID = 1L;

序列化对象

1
2
3
4
FileOutputStream fout = new FileOutputStream("object.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(object);
oos.close();

反序列化对象

1
2
3
4
FileInputStream fin = new FileInputStream("object.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
Object object = ois.readObject();
ois.close();

最佳实践

  • 显式声明 serialVersionUID:强烈建议所有可序列化类显式声明 serialVersionUID,以确保在不同的Java编译器实现中具有一致的值。
  • 版本控制:如果类的当前版本与之前版本不兼容,应递增 serialVersionUID
  • 使用 @SuppressWarnings:如果不使用序列化进行永久存储,可以使用 @SuppressWarnings("serial") 来避免警告。

常见问题

1. 为什么自动生成的 serialVersionUID 不可靠?

自动生成的 serialVersionUID 计算高度依赖于类的细节,这些细节可能因编译器实现而异,从而导致反序列化时出现意外的 InvalidClassException

2. 什么时候需要更改 serialVersionUID

当类的结构发生更改,导致序列化数据不兼容时,需要更改 serialVersionUID。但在更改之前,应探索序列化的广泛对象版本控制支持,尽量通过自定义 readObjectwriteObject 方法等方式保持兼容性。

3. 如果忘记更新 serialVersionUID 会怎样?

可能会导致不同结构的类具有相同的 serialVersionUID,默认机制将无法检测到差异,尝试反序列化不兼容的数据时,可能会出现难以查找的运行时错误或静默失败。


What is a serialVersionUID and why should I use it?
https://119291.xyz/posts/2025-04-22.what-is-a-serialversionuid-and-why-should-i-use-it/
作者
ww
发布于
2025年4月23日
许可协议