大致翻译自Understand the serialVersionUID
Understand the serialVersionUID
如果你曾经实现过Serializable接口,你肯定遇到过如下的警告信息
The serializable class xxx does not declare a static final serialVersionUID field of type long
So…what is serialVersionUID?
在一个Serializable
类中,serialVersionUID
被用来做版本控制。如果你不显式声明一个serialVersionUID
,JVM会基于你的Serializable
类的各个方面,自动为你生成。参见Java(TM) Object Serialization Specification
1. SerialVersionUID例子
上面的说明在开始时可能有点难以理解(至少我是这样的),让我们开始用一个例子来了解Serializable
类,以及如何使用SerialVersionUID
实现版本控制。
1.1 Address.java
一个serialVersionUID
为1L
的序列化类
import java.io.Serializable;
public class Address implements Serializable{
private static final long serialVersionUID = 1L;
String street;
String country;
public void setStreet(String street){
this.street = street;
}
public void setCountry(String country){
this.country = country;
}
public String getStreet(){
return this.street;
}
public String getCountry(){
return this.country;
}
@Override
public String toString() {
return new StringBuffer(" Street : ")
.append(this.street)
.append(" Country : ")
.append(this.country).toString();
}
}
1.2 WriteObject.java
一个简单的类写入/序列化Address
对象到文件c:\\address.ser
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class WriteObject{
public static void main (String args[]) {
Address address = new Address();
address.setStreet("wall street");
address.setCountry("united states");
try{
FileOutputStream fout = new FileOutputStream("c:\\address.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(address);
oos.close();
System.out.println("Done");
}catch(Exception ex){
ex.printStackTrace();
}
}
}
1.3 ReadObject.java
一个简单的类从文件c:\\address.ser
读取/反序列化Address
对象
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ReadObject{
public static void main (String args[]) {
Address address;
try{
FileInputStream fin = new FileInputStream("c:\\address.ser");
ObjectInputStream ois = new ObjectInputStream(fin);
address = (Address) ois.readObject();
ois.close();
System.out.println(address);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
2. Testing
做一些测试来演示serialVersionUID
的使用
2.1 同样的serialVersionUID
同样的serialVersionUID
,在反序列化的过程中是没有问题的
javac Address.java
javac WriteObject.java
javac ReadObject.java
java WriteObject
java ReadObject
Street : wall street Country : united states
2.2 不同的serialVersionUID
在Address.java中,把serialVersionUID
改变为 2L(原来为1L),再次编译:
javac Address.java
java ReadObject
java.io.InvalidClassException: Address; local class incompatible:
stream classdesc serialVersionUID = 1, local class serialVersionUID = 2
...
at ReadObject.main(ReadObject.java:14)
会抛出InvalidClassException
异常,因为你写的序列化类serialVersionUID
为1L
,却尝试使用更新的序列化类SerialVersionUID
为2L
来检索它
serialVersionUID
在序列化和反序列化过程中必须匹配。什么时候应该更新你的serialVersionUID?
当序列化类中有一些不兼容的Java类型改变时,序列化被改变,必须更新serialVersionUID
。(When your serialization class is updated with some incompatible Java type changes to a serializable class, you have to update your serialVersionUID.)
有关可序列化类的兼容和不兼容的Java类型更改的详细信息,请参阅Java对象序列化规范。
3. 缺省的serialVersionUID是怎么回事?
如果没有声明serialVersionUID
,JVM将使用自己的算法来生成一个默认的SerialVersionUID
,你可以在这里检查算法。
缺省的serialVersionUID
计算对类详细信息非常敏感,并且可能因不同的JVM实现而异,并在反序列化过程中导致意外的InvalidClassExceptions
。
3.1 Client / Server 环境
- 在Windows中,客户端正中使用SUN的JVM。
- 在Linux中,服务器使用JRockit。
客户端通过socket将具有默认生成的serialVersionUID
(例如123L)的可序列化类发送到服务器,服务器可能在反序列化过程中生成不同的serialVersionUID
(例如124L),并抛出异常InvalidClassExceptions
。
3.2 File / Database 环境
- 在Windows中,应用#1使用SUN的JVM。
- 在Linux中,应用#2使用JRockit。
序列化可被保存到文件或数据库中。某些情况下,应用#1将默认生成的serialVersionUID
(例如123L)序列化类存储到数据库中,而在编译过程中,App#2可能会生成不同的serialVersionUID
(例如124L),并抛出异常InvalidClassExceptions
。
你可以在此处查看JVM实现列表
4. 如何产生serialVersionUID
可以使用JDK serialver
或Eclipse IDE自动生成serialVersionUID
,详细信息。
总结
SUN是强烈建议开发人员声明serialVersionUID
以避免上面列出的不同的JVM问题,但是我建议你理解什么是序列化,serialVersionUID
如何实现版本控制以及为什么你的类需要使用序列化。了解serialVersionUID
的概念比任何建议更好。
序列化
对象序列化(object serialization)API,它提供了一个框架,用来将对象编码成字节流,并从字节流编码中重新构建对象。
将一个对象编码成一个字节流,称作该对象序列化(serializing),想反的处理过程被称为反序列化(deserializing)