java序列化之serialVersionUID

上次去面试的时候被问到一个关于序列化的问题:序列化怎么解决版本问题,即在我序列化之后,如果我又改了被序列化的那个类,我怎么反序列化它。当时被问懵了,回来也忘了查。今天在做一个java web项目时,在servlet中没有定义serialVersionUID而被eclipse标记为黄色提示,一直不懂这个serialVersionUID是干啥子用的,于是趁有空就搜了一下,发现它就是解决序列化版本问题的关键。

还是用例子来说明吧,首先建一个普通类:

import java.io.*;
public class Person implements Serializable{
	private String name;
	public Person(String name){
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String toString() {
		return "name=" + name;
	}
}
然后序列化这个类:

import java.io.*;
public class WhySerialversionUID_Write {
	public static void main(String[] args) throws Exception{
		Person p = new Person("zhengjunyan");
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt"));
		oos.writeObject(p);	
		oos.close();
	}
}
再反序列化它:

import java.io.*;
public class WhySerialversionUID_Read{
	public static void main(String[] args)  throws Exception{
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt"));
		Person p = (Person)ois.readObject();
		System.out.println(p);
	}
}
此时一切正常:


然后再修改Person类,如添加一个字段:

import java.io.*;
public class Person implements Serializable{
	private String name;
	private int age;//添加了这个字段
	public Person(String name){
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String toString() {
		return "name=" + name;
	}
}
再次反序列化,将会报如下错误:


它说字节流中的serialVersionUID跟我们本地这个Person类的serialVersionUID不匹配。原来,java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本的一致性的,在进行反序列化时,jvm会把传来的字节流中的serialVersionUID跟本地相应的类的serialVersionUID作比较,如果不相同则不会进行反序列化。

那如果我们在序列化后想对类增加字段或方法等怎么办呢?我们可以给类定义一个serialVersionUID字段并赋值,因为java在序列化某个类时,如果我们给类的serialVersionUID赋了值,他就会用我们给定的这个serialVersionUID来标示版本,如果我们没有给,他就会自动给这个class执行某个算法,得到一个serialVersionUID,这样的话只有类保存不变,它的serialVersionUID才会相同。

所以如果要保证修改后的版本能被正常的反序列化,可以给类添加一个字段serialVersionUID,例如:

import java.io.*;
public class Person implements Serializable{
	private static final long serialVersionUID = 1L;
	private String name;
	//private int age;
	public Person(String name){
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String toString() {
		return "name=" + name;
	}
}
跟上面同样的做法,先序列化再反序列化,正常;再去掉Person类的注释,即添加一个字段age,再反序列化,仍然正常。

这也解释了eclipse为什么要我们为servlet添加字段serialVersionUID的原因了,因为java的HttpServlet(当然还有其父类等)实现了serializable接口,而根据上面所述,若实现了序列化接口,为了版本的维护需要,最好是给定一个serialVersionUID字段。

你可能感兴趣的:(Java)