java对象序列化为什么要使用SerialversionUID
首先谈谈问什么要序列化对象?
- 把对象转换为字节序列的过程称为对象的序列化
- 把字节序列恢复为对象的反序列化
对象序列化主要有两种用途:
- 把对象的字节序列永久的保存到硬盘上,通常存放在一个文件中
- 在网络上传送对象的字节序列
在很多应用中,需要对某些对象进行序列化,让他们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的就说web服务器中的session对象,当有10w用户并发访问,就有可能出现10w个session对象,内存可能吃不消,于是web容器就会把一些session先序列化到硬盘中,等要用了,再把保存再硬盘中的对象还原到内存中
当两个进程再进行远程通信的时候,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式再网络上传送,发送方需要把这个java对象转换为字节序列,才能再网络上传送,接收方则需要把字节序列再恢复为java对象
为什么要用SerialversionUID呢?
简单看下Serializable接口的说明
If a serializable class does not explicitly declare a serialVersionUID,
then the serialization runtime will calculate a default
serialVersionUID value for that class based on various aspects of the class,
as described in the Java(TM) Object Serialization Specification.
如果用户没有自己声明一个SerialversionUID,接口会默认生成一个SerialversionUID
但是,非常强烈建议用户自己定义一个SerialversionUID,因为默认的SerialversionUID对于class的细节非常敏感,反序列化的时候可能会导致InvalidClassException这个异常
使用默认的SerialversionUID
public class Person implements Serializable {
private int age;
private String name;
private String sex;
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
然后去序列化和反序列化
public class TestSerialversionUID {
public static void main(String[] args) throws Exception {
serializablePerson();
Person person=deserializablePerson();
System.out.println(person.getName()+" "+ person.getAge());
}
private static void serializablePerson() throws Exception{
Person person=new Person();
person.setName("测试实例");
person.setAge(21);
person.setSex("male");
ObjectOutputStream oos = new ObjectOutputStream
(new FileOutputStream(new File("E:/person.txt")));
oos.writeObject(person);
System.out.println("序列化成功");
oos.close();
}
private static Person deserializablePerson() throws Exception {
ObjectInputStream ois = new ObjectInputStream
(new FileInputStream(new File("E:/person.txt")));
Person person=(Person)ois.readObject();
System.out.println("反序列化成功");
ois.close();
return person;
}
}
测试结果如下图:
序列化成功
反序列化成功
测试实例 21
如果我们先尽心序列化,然后再反序列化之前修改了Person类会怎么样呢?
/**
* @author wcc
* @date 2021/8/18 20:05
*/
public class Person implements Serializable {
private int age;
private String name;
private String sex;
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
可以看到,当我们子反序列化之前修改Person类的时候,Person类对应的SerialversionUID也变化了,而序列化和反序列化就说通过对比其SerialversionUID来进行的,一旦SerialversionUID不匹配,反序列化就无法成功。再实际的生产环境中,我们可能会建一系列的中间Object来反序列化我们的pojo,为了解决这个问题,我们就需要再实体类中自定义SerialversionUID。
在Person类中加入自定义SerialversionUID
public class Person implements Serializable {
private static final long serialVersionUID = -5809782578272943999L;
private int age;
private String name;
private String sex;
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
/*@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}*/
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
不管我们序列化之后如何更改我们的Person类(不删除原有字段),最终都可以反序列化成功。