SerialVersionUID
SerialVersionUID就是一个静态常量
-
一般都是声明在一个可序列化的类中,并且一般都是显示的进行声明
-
声明的格式一般为:
private static long SerialVersionUID = xxxxxxxxxxxxxxxxxxxxxL
-
SerialVersionUID用来表示类的不同版本的兼容性,以序列化对象进行版本控制,有关对象反序列化时是否兼容
如果没有显示的在类中声明这个静态常量,那么java运行时环境(jre)会根据类的内部细节自动进行生成
- 但是如果是java运行时环境自动生成的SerialVersionUID,那么这个时候如果我们对这个类的实例变量进行了修改,这个时候java运行时环境会重新生成一个SerialVersionUID ---- 也就是如果我们没有显示的声明这个静态常量,那么这个常量就有可能发生改变
- 所以我们最好显示的声明这个静态常量
难点思路分析:
如果一个Person类没有显示的声明SerialVersionUID,但是这个类实现了Serializable接口,这个时候我们对Person类的一个对象进行了序列化(也就是将这个对象持久化到了硬盘中),然后我们在这个类中多定义一个成员变量,然后再进行反序列化,这个时候就不能将原本的对象从硬盘中还原回来了,这个时候会抛出一个运行时异常 — java.io.InvalidClassException
-
那么为什么反序列化会失败?
因为在序列化之后,反序列化之前我们对这个类中的成员变量进行了修改(也就是修改了实例变量),这个时候这个类的序列化ID(SerialVersionUID)就会跟着发生改变,这个时候我们虽然将原本未修改的类的对象持久化存储到了硬盘中,但是这个时候我们反序列化的时候由于序列化ID发生了改变,这个时候就找不到原本的Person类了,这个时候也就自然反序列化失败了 — 也就相当于我们持久化存储的时候是将这个对象的一些基本的信息存储到了硬盘中,这个时候我们的类就是模板,我们想要反序列化这个时候先要找到模板,然后根据这个模板,然后加上零件,这个时候才可以合成一个对象 – 而这个时候我们只有硬盘中的文件,也就是只有零件 , 由于序列化ID发生了改变,这个时候不能找到对应的类,这个时候就没有模板,自然就合成不成一个java对象了
对应思路我们给出一个例题:
这里我们先提供一个Person类
- 首先,这个Person类实现Serializable接口(Serializable接口是一个标识接口 — 标识可以序列化的类)
- 其次,这个类中我们不显示的提供SerialVersionUID
package IO流.SerialVersionUID;
import java.io.Serializable;
public class Person implements Serializable{
private String name;
private int age;
//无参构造方法
public Person(){
}
//有参构造方法
public Person(String name, int age){
this.name = name;
this.age = age;
}
/*
对应私有属性的get和set方法
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写toSting()方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后我们以这个Person类为模板创建一个对象,将这个类的对象序列化到硬盘中
package IO流.SerialVersionUID;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.File;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) throws IOException{
//创建Person类的对象
Person p = new Person("飞飞",14);
/*
创建对象输出流对象
注意: 虽然大多的处理流创建对象时都不会抛出异常,但是我们的对象输入输出流会抛出IOException
*/
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("Person2.dat")));
/*
将我们的Person类型的对象p序列化到硬盘中
*/
oos.writeObject(p);
/*
流资源的关闭
*/
oos.close();
}
}
然后我们再对Person类进行修改: 我们给Person类添加一个成员变量:private int id
package IO流.SerialVersionUID;
import java.io.Serializable;
public class Person implements Serializable{
private String name;
private int age;
//添加的成员变量: private int id
private int id;
//无参构造方法
public Person(){
}
//有参构造方法
public Person(String name, int age){
this.name = name;
this.age = age;
}
/*
对应私有属性的get和set方法
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//重写toSting()方法
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
最后我们再进行反序列化得到我们持久化存储到硬盘中的Person类的对象
package IO流.SerialVersionUID;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.File;
import java.io.IOException;
//java.lang包中的内容会自动导入,但是为了清楚,还是手动到了一次,实际编程中不需要再导入
import java.lang.ClassNotFoundException;
public class Demo2 {
public static void main(String[] args) throws IOException,ClassNotFoundException{
//创建对象输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("Person2.dat")));
/*
从文件中进行反序列化,尝试得到Person类的对象
注意: 对象输入流的readObject()方法不但会抛出IOException,还会抛出ClassNotFoundException
*/
Person p = (Person)ois.readObject();
/*
输出Person类的对象p
*/
System.out.println(p);
/*
流资源的关闭
*/
ois.close();
}
}
- 这个时候进行反序列化的时候会抛出一个java.io.InvalidClassException
那么我们如何避免这种情况的发生呐?
我们只需创建Person类的时候显示的提供SerialVersionUID就可以了
- 我们只要显示的提供了SerialVersionUID,这个时候不管我们的Person类如何改变,这个时候这个类的序列化版本号都不会发生改变 — 也就不会出现这种异常