说明:这篇文章关于IO流,序列化serialVersionUID的功能知识没有介绍,若没有相关基础,遇到不懂请自行Google。
问题
这几天在复习序列化时,发现了我项目中存在的一个隐藏bug。
我项目中的entity都继承于一个基类BaseEntity,这个类大概是这样的:
class BaseEntity implements Serializable{
public static final long serialVersionUID = 1L;
//属性...
}
因此,我在没有测试的情况下,误以为子类继承了这个BaseEntity,那么子列就不用再添加这个serialVersionUID了。
其实,这是不对的,虽然子类继承了这个serialVersionUID,但是,jvm还是会为子类这个对象重新赋值一个serialVersionUID,除非你在子类重新显式声明一个serialVersionUID。
测试过程:
测试代码:
注意:注释的代码之后会用到
public class PrivateTest extends Base{
// private static final long serialVersionUID = 1L;
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// private boolean gender;
//
// public boolean isGender() {
// return gender;
// }
//
// public void setGender(boolean gender) {
// this.gender = gender;
// }
@Override
public String toString() {
return "PrivateTest{" +
"id=" + id +
", name='" + name + '\'' +
// ", gender='" + gender + '\'' +
'}';
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("./text.txt");
myWrite(file);
// myRead(file);
}
private static void myRead(File file) throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
PrivateTest o = (PrivateTest)in.readObject();
System.out.println(o.toString());
in.close();
}
private static void myWrite(File file) throws IOException {
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file));
PrivateTest test = new PrivateTest();
test.setId(1);
test.setName("abc");
out.writeObject(test);
out.close();
}
}
class Base implements Serializable{
public static final long serialVersionUID = 1L;
}
一开始,我们运行上面的代码的myWrite方法,往文件写入一个对象,接着,我们把gender属性的几行注释代码恢复,运行myRead方法,会发现读取报错InvalidClassException。
报错如下:
Exception in thread "main" java.io.InvalidClassException:
privatetest.PrivateTest; local class incompatible:
stream classdesc serialVersionUID = -9051711779420750007,
local class serialVersionUID = 7378139454451283922
发现本地和流中的serialVersionUID不同,说明我们的privateTest对象自身没有serialVersionUID,也就是父类的serialVersionUID没有发挥作用。
那么,接下来我们在PrivateTest类中添加上serialVersionUID之后,再重复上面的操作:先运行myWrite方法,写入对象,然后取消对gender属性的注释,运行myRead方法,会发现可以成功打印出对象信息,说明修改成功。
成功打印对象信息:
PrivateTest{id=1, name='abc', gender='false'}
总结
总结一下,子类继承父类时,若父类实习Serializable接口,同时有serialVersionUID,子类也需要声明serialVersionUID,否则序列化读取写入对象时,可能会报错InvalidClassException。