总述
下面我通过三种场景来表现出Serializable与serialVersionUID的作用。
- 没有实现序列化接口Serializable的场景
- 实现了序列化接口Serializable,但没指定serialVersionUID值的场景
- 实现了序列化接口Serializable,并指定serialVersionUID值的场景
一、未实现序列化接口场景
实体类Student没有实现Serializable接口
1.实体类
import java.io.Serializable;
/**
* @author: kqyin
* @date: 2021/11/23 17:04
* @Description: 学生
*/
public class Student /*implements Serializable*/ {
//private static final long serialVersionUID = -1001598034465383803L;
// id
private String id;
// 姓名
private String userName;
// 性别
private boolean sex;
// 手机号
// private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
/* public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}*/
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", sex=" + sex +
'}';
}
}
2.测试类
import java.io.*;
/**
* @author: kqyin
* @date: 2021/11/23 17:04
* @Description: 序列化测试
*/
public class SerialVersionUIDTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writeObject();
readObject();
}
/**
* @Author kqyin
* @Date 2021/11/23 17:22
* @Description 将object序列化写到文件中
*/
private static void writeObject() throws IOException {
Student student = new Student();
student.setId("100000");
student.setUserName("张三");
student.setSex(true);
FileOutputStream fileOutputStream = new FileOutputStream("D:\\a.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(student);
}
/**
* @Author kqyin
* @Date 2021/11/23 17:23
* @Description 将文件中的对象读取成对象
*/
private static void readObject() throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("D:\\a.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Student student1 = (Student) objectInputStream.readObject();
System.out.println(student1.toString());
}
}
3.序列化报错
由于实体类Student未实现Serializable接口,因此在将对象序列化时,报错
Exception in thread "main" java.io.InvalidClassException: Student; class invalid for deserialization
at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:169)
at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:885)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2099)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1624)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:464)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at SerialVersionUIDTest.readObject(SerialVersionUIDTest.java:40)
at SerialVersionUIDTest.main(SerialVersionUIDTest.java:13)
二、实现序列化接口,不给定serialVersionUID场景
1.实体类
实体类Student实现Serializable接口,但未给定serialVersionUID值。
import java.io.Serializable;
/**
* @author: kqyin
* @date: 2021/11/23 17:04
* @Description: 学生
*/
public class Student implements Serializable {
//private static final long serialVersionUID = -1001598034465383803L;
// id
private String id;
// 姓名
private String userName;
// 性别
private boolean sex;
// 手机号
// private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
/* public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}*/
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", sex=" + sex +
'}';
}
}
2.正常序列化和反序列化
执行测试方法,可以正常序列化和反序列化
3.给实体类增加字段
给实体类Student增加字段phone
import java.io.Serializable;
/**
* @author: kqyin
* @date: 2021/11/23 17:04
* @Description: 学生
*/
public class Student implements Serializable {
//private static final long serialVersionUID = -1001598034465383803L;
// id
private String id;
// 姓名
private String userName;
// 性别
private boolean sex;
// 手机号
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", sex=" + sex +
'}';
}
}
4.反序列化报错
只执行测试类中readObject方法,进行反序列化,提示serialVersionUID值不同。这是因为没有指定serialVersionUID,serialVersionUID由于实体类的结构变动,计算的serialVersionUID值不同,导致反序列化失败。
Exception in thread "main" java.io.InvalidClassException: Student; local class incompatible: stream classdesc serialVersionUID = -1001598034465383803, local class serialVersionUID = -254405469545206643
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1941)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1807)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2098)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1624)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:464)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at SerialVersionUIDTest.readObject(SerialVersionUIDTest.java:37)
at SerialVersionUIDTest.main(SerialVersionUIDTest.java:11)
三、实现序列化接口,并给定serialVersionUID场景
1.实体类
实体类实现Serializable接口,并指定serialVersionUID的值,此时没有phone字段。
import java.io.Serializable;
/**
* @author: kqyin
* @date: 2021/11/23 17:04
* @Description: 学生
*/
public class Student implements Serializable {
private static final long serialVersionUID = -1001598034465383803L;
// id
private String id;
// 姓名
private String userName;
// 性别
private boolean sex;
// 手机号
//private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
/* public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}*/
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", sex=" + sex +
'}';
}
}
2.只进行序列化
只执行测试类writeObject()方法,进行序列化。
3.给实体增加字段
给实体类Student增加phone字段,改变toString方法,使得结果更清晰,也可以不改变。
import java.io.Serializable;
/**
1. @author: kqyin
2. @date: 2021/11/23 17:04
3. @Description: 学生
*/
public class Student implements Serializable {
private static final long serialVersionUID = -1001598034465383803L;
// id
private String id;
// 姓名
private String userName;
// 性别
private boolean sex;
// 手机号
private String phone;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public boolean isSex() {
return sex;
}
public void setSex(boolean sex) {
this.sex = sex;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", sex=" + sex +
", phone='" + phone + '\'' +
'}';
}
}
4.反序列化成功
可以看到即使增加phone字段,并且修改了类里的方法,依旧反序列化成功。
总结
通过上述三个场景的试验,可以得知
- 实现Serializable接口,标注该实体类可以被序列化
- serialVersionUID指定唯一值,使结构变动的类依旧可以反序列化成功
- 如果serialVersionUID值发生变动,会导致反序列化失败