Skip to content

Commit 467a3c9

Browse files
author
zhupeiquan
committed
What is a serialVersionUID and why should I use it?
1 parent 5cce1f1 commit 467a3c9

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
# serialVersionUID 有什么作用?该如何使用?
2+
3+
当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何 serialVersionUID ?
4+
5+
serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途:
6+
7+
- 把对象序列化成字节码,保存到指定介质上(如磁盘等)
8+
- 用于网络传输
9+
10+
现在反过来说就是,serialVersionUID 会影响到上述所提到的两种行为。那到底会造成什么影响呢?
11+
12+
[java.io.Serializable](http://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html) doc 文档,给出了一个相对详细解释:
13+
serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,如下:
14+
```
15+
ANY-ACCESS-MODIFIER static final long serialVersionUID = 1L;
16+
```
17+
18+
文档上还建议 serialVersionUID 置为 private。
19+
20+
举例说明如下:
21+
现在尝试从将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 两日如下:
22+
```
23+
public class Person implements Serializable {
24+
25+
private static final long serialVersionUID = 1L;
26+
27+
private String name;
28+
private Integer age;
29+
private String address;
30+
31+
public Person() {
32+
}
33+
34+
public Person(String name, Integer age, String address) {
35+
this.name = name;
36+
this.age = age;
37+
this.address = address;
38+
}
39+
40+
41+
@Override
42+
public String toString() {
43+
return "Person{" +
44+
"name='" + name + '\'' +
45+
", age=" + age +
46+
", address='" + address + '\'' +
47+
'}';
48+
}
49+
}
50+
```
51+
52+
简单的测试一下:
53+
```
54+
@Test
55+
public void testversion1L() throws Exception {
56+
File file = new File("person.out");
57+
// 序列化
58+
ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));
59+
Person person = new Person("John", 21, "广州");
60+
oout.writeObject(person);
61+
oout.close();
62+
// 反序列化
63+
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
64+
Object newPerson = oin.readObject();
65+
oin.close();
66+
System.out.println(newPerson);
67+
}
68+
```
69+
70+
测试发现没有什么问题。有一天,因发展需要, 需要在 Person 中增加了一个字段 email,如下:
71+
```
72+
public class Person implements Serializable {
73+
74+
private static final long serialVersionUID = 1L;
75+
76+
private String name;
77+
private Integer age;
78+
private String address;
79+
private String email;
80+
81+
public Person() {
82+
}
83+
84+
public Person(String name, Integer age, String address) {
85+
this.name = name;
86+
this.age = age;
87+
this.address = address;
88+
}
89+
90+
public Person(String name, Integer age, String address,String email) {
91+
this.name = name;
92+
this.age = age;
93+
this.address = address;
94+
this.email = email;
95+
}
96+
97+
@Override
98+
public String toString() {
99+
return "Person{" +
100+
"name='" + name + '\'' +
101+
", age=" + age +
102+
", address='" + address + '\'' +
103+
", email='" + email + '\'' +
104+
'}';
105+
}
106+
}
107+
```
108+
109+
这时我们假设和之前序列化到磁盘的 Person 类是兼容的,便不修改版本标识 serialVersionUID。再次测试如下
110+
```
111+
@Test
112+
public void testversion1LWithExtraEmail() throws Exception {
113+
File file = new File("person.out");
114+
ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));
115+
Object newPerson = oin.readObject();
116+
oin.close();
117+
System.out.println(newPerson);
118+
}
119+
```
120+
将以前序列化到磁盘的旧 Person 反序列化到新 Person 类时,没有任何问题。
121+
122+
可当我们增加 email 字段后,不作向后兼容。即放弃原来序列化到磁盘的 Person 类,这时我们可以将版本标识提高,如下:
123+
```
124+
private static final long serialVersionUID = 2L;
125+
```
126+
127+
再次进行序列化,则会报错,如下:
128+
```
129+
java.io.InvalidClassException:Person local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
130+
```
131+
132+
谈到这里,我们大概可以清楚,serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的,则不修改 serialVersionUID;反之,则提高 serialVersionUID的值。再回到一开始的问题,为什么 ide 会提示声明 serialVersionUID 的值呢?
133+
134+
因为若不显式定义 serialVersionUID 的值,Java 会根据类的细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。
135+
136+
附录拓展:
137+
138+
- [深入理解 Java 对象序列化](http://developer.51cto.com/art/201202/317181.htm)
139+
- [对象的序列化和反序列化](http://www.blogjava.net/lingy/archive/2008/10/10/233630.html)
140+
141+
stackoverflow原址:http://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it

0 commit comments

Comments
 (0)