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

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

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.序列化版本号的作用

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

在这里插入图片描述在这里插入图片描述
三年前小蓝在开发某个项目的时候写了一个学生类:

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 + '\'' +
                '}';
    }
}

在这里插入图片描述

三年后,小蓝跑路了。来了个新人小红,由于业务需求,小红在小蓝写的类上增加了一个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.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)

在这里插入图片描述
小红请教了她的师傅,师傅告诉她:加一个序列化版本号就行了
小红照做了:

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 +
                '}';
    }
}

在这里插入图片描述
小红运行了代码,成功地跑了起来,小红这才放心地下班了

在这里插入图片描述

  • 可以看到一个继承了 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
    在这里插入图片描述

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

  • 如果出现以下异常信息:
    在这里插入图片描述
    在这里插入图片描述
  • 要把stream classdesc serialVersionUID复制到你序列化的那个类中
    在这里插入图片描述
  • 别忘了加private static final long
  • 数字后面还要加一个L,表示long类型
  • 比如
    在这里插入图片描述

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

  • 比如:
    在这里插入图片描述


  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值