【JAVA】关于java反序列化失败的问题

最近刚好在打java基础,但是用ObjectOutputStream进行反序列的时候
遇到了反序列化失败的问题,所以在这里讨论一下反序列化失败的问题

目录

  • 1. 反序列化失败抛出的异常信息
  • 2. 在java中类的区分
    • 1.serializable接口
    • 2.java中类的区分
  • 3. 序列化版本号
    • 1.序列化版本号的作用
    • 2.手动创建序列化版本号
  • 4. 解决反序列化失败的方法

1. 反序列化失败抛出的异常信息

java.io.InvalidClassException: javaSE.bean.Student; local class incompatible: stream classdesc serialVersionUID = -4563547663326088190, local class serialVersionUID = -27206582129922100
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2001)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2158)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1665)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:501)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:459)

异常信息:

  • local class incompatible
  • 本地的类不匹配
  • stream classdesc serialVersionUID = -4563547663326088190
  • 在流中类的序列化版本号是-4563547663326088190
  • local class serialVersionUID = -27206582129922100
  • 本地类的序列化版本号是-27206582129922100

很明显抛出异常的原因就是:
本地类的序列化版本号和我们进行序列化时的序列化版本号不同
所以要搞清楚这个异常就要搞清楚序列化版本号是个什么东西


2. 在java中类的区分

1.serializable接口

我们在进行序列化的时候会让某个类去实现serializable接口
比如我有一个student类去实现serializable接口:

public class Student implements Serializable {
    private int age;
    private String name;
    
    public Strudent(int age, String name){
    this.age = age;
    this.name = name;
    }
    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
  • serializable接口是一个标志接口,在类中我们并没有实现任何方法
  • 实现serializable接口是因为jvm会自动分配一个序列化版本号

2.java中类的区分

  • 第一,以类名来区分。类名不一样,肯定不是同一个类。
  • 第二, 如果类名一样,靠序列化版本号区分。

3. 序列化版本号

读到这里你肯定对序列化版本号多少有点了解了,下面是对序列化版本号的具体说明

1.序列化版本号的作用

在讲清楚序列化版本号之前,我们来看个故事:

【JAVA】关于java反序列化失败的问题_第1张图片【JAVA】关于java反序列化失败的问题_第2张图片
三年前小蓝在开发某个项目的时候写了一个学生类:

public class Student implements Serializable {
    private int age;
    private String name;
    
    public Strudent(int age, String name){
    this.age = age;
    this.name = name;
    }
    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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

【JAVA】关于java反序列化失败的问题_第3张图片

三年后,小蓝跑路了。来了个新人小红,由于业务需求,小红在小蓝写的类上增加了一个id属性

public class Student implements Serializable {
    private int age;
    private String name;
    private int id;

    public Student(int age, String name, int id) {
        this.age = age;
        this.name = name;
        this.id = id;
    }

    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 int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

【JAVA】关于java反序列化失败的问题_第4张图片

当小红改完代码,重新运行程序时,项目跑不了,抛出的异常让小红懵逼了

java.io.InvalidClassException: javaSE.bean.Student; local class incompatible: stream classdesc serialVersionUID = -4563547663326088190, local class serialVersionUID = -27206582129922100
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2001)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2158)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1665)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:501)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:459)

【JAVA】关于java反序列化失败的问题_第5张图片
小红请教了她的师傅,师傅告诉她:加一个序列化版本号就行了
小红照做了:

public class Student implements Serializable {
    private static final long serialVersionUID = 7471391170055841173L;
    private int age;
    private String name;
    private int id;

    public Student(int age, String name, int id) {
        this.age = age;
        this.name = name;
        this.id = id;
    }

    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 int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}

【JAVA】关于java反序列化失败的问题_第6张图片
小红运行了代码,成功地跑了起来,小红这才放心地下班了

【JAVA】关于java反序列化失败的问题_第7张图片

  • 可以看到一个继承了 serializable 接口的类如果不加上一个 serialVersionUID
    用 ObjectInputStream 进行序列化之后,如果这个类有一点改变,反序列化就会失败
  • 序列化版本号的作用就是告诉 java 虚拟机 改变之前的类和改变之后的类是同一个类
  • 比如上面的例子:小蓝三年前写的 Student 和小红改的 Student 类是同一个类,但是却反序列化失败了
    因为 Student 类继承了 serializable 接口,会自动分配一个 serialVersionUID。
    但是因为小红把代码改了,在重新编译运行的时候,又会分配一个serialVersionUID
    导致之前序列化时类的 serialVersionUID 和反序列化时类 serialVersionUID 所以就抛出了异常

2.手动创建序列化版本号

对于序列化版本号的并没有多大讲究,
在实现了serializable接口的类中添加一个serialVersionUID即可
只要保证在一个项目中序列化版本号不要重复就行

  • 比如:
private static final long serialVersionUID = 1L;
  • 又或者:
private static final long serialVersionUID = 7471391170055841173L;
  • idea中可以选中类名按Alt + Enter即可自动生成serialVersionUID
    【JAVA】关于java反序列化失败的问题_第8张图片

4. 解决反序列化失败的方法

  • 如果出现以下异常信息:
    【JAVA】关于java反序列化失败的问题_第9张图片
    【JAVA】关于java反序列化失败的问题_第10张图片
  • 要把stream classdesc serialVersionUID复制到你序列化的那个类中
    在这里插入图片描述
  • 别忘了加private static final long
  • 数字后面还要加一个L,表示long类型
  • 比如
    【JAVA】关于java反序列化失败的问题_第11张图片

  • 为了避免反序列化失败情况的发生,一个类实现了serializable接口就一定要添加一个serialVersionUID

  • 比如:
    【JAVA】关于java反序列化失败的问题_第12张图片


你可能感兴趣的:(JavaSE,java)