修改serialVersionUID以解决InvalidClassException反序列化异常

文章讲述了在Java开发中,由于没有指定serialVersionUID而导致序列化后数据无法反序列化的现象,介绍了如何使用CompatibleInputStream解决此问题,以及更优的解决方案是预先设置serialVersionUID值。
摘要由CSDN通过智能技术生成

前言

开发过程中某个类并没有指定serialVersionUID,并且该类是通过序列化存储在数据库中的,并没有转换为json存储,就导致在该类添加新字段之后,原来的数据不能进行反序列化,提示反序列化异常。

复现过程

创建测试类
@Data
public class User implements Serializable {

    private String userName;

    private String age;

}
写入序列化对象到文件

public static void main(String[] args) throws IOException, ClassNotFoundException {
    User user = new User();
    user.setUserName("张三");
    user.setAge("18");
    // 写入序列化数据
    writeObject(user);
}


/**
 * 写入序列化
 *
 * @param obj 需要写入的对象
 */
public static Path writeObject(Object obj) throws IOException {
    Path path = getPath();
    try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(path))) {
        objectOutputStream.writeObject(obj);
    }
    return path;
}


public static Path getPath() {
    return Paths.get("G:\\临时\\1");
}
测试读取
public static void main(String[] args) throws IOException, ClassNotFoundException {
    Object object = readObject(getPath());
    // 输出结果: User(userName=张三, age=18)
    System.out.println(object);
}


/**
 * 读取序列化对象
 *
 * @return 反序列化之后的对象
 */
public static Object readObject(Path path) throws IOException, ClassNotFoundException {
    ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(path));
    return objectInputStream.readObject();
}
修改serialVersionUID
@Data
public class User implements Serializable {
	// 添加serialVersionUID字段
    private static final long serialVersionUID = 1L;

    private String userName;

    private String age;

}
测试读取
public static void main(String[] args) throws IOException, ClassNotFoundException {
    Object object = readObject(getPath());
    // 触发异常:local class incompatible: stream classdesc serialVersionUID = 7769644417338494813, local class serialVersionUID = 1
    System.out.println(object);
}
忽略serialVersionUID读取类信息
public class CompatibleInputStream extends ObjectInputStream {

    public CompatibleInputStream(InputStream in) throws IOException {
        super(in);
    }

    @Override
    protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
        ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();
        Class localClass;
        try {
            localClass = Class.forName(resultClassDescriptor.getName());
        } catch (ClassNotFoundException e) {
            return resultClassDescriptor;
        }
        ObjectStreamClass localClassDescriptor = ObjectStreamClass.lookup(localClass);
        if (localClassDescriptor != null) {
            final long localSUID = localClassDescriptor.getSerialVersionUID();
            final long streamSUID = resultClassDescriptor.getSerialVersionUID();
            if (streamSUID != localSUID) {
                try {
                    Field suid = resultClassDescriptor.getClass().getDeclaredField("suid");
                    suid.setAccessible(true);
                    suid.set(resultClassDescriptor, localSUID);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return resultClassDescriptor;
    }
}
编写新读取方法
/**
 * 获取序列化数据
 *
 * @return 反序列化之后的对象
 */
public static void getObject(Path path) throws IOException, ClassNotFoundException {
    InputStream inputStream = Files.newInputStream(path);
    // 解析反序列化数据
    ObjectInputStream s = new CompatibleInputStream(inputStream);
    // 读取对象
    Object readObject = s.readObject();
    System.out.println(readObject);
}
测试新读取方法
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 输出 User(userName=张三, age=18)
    getObject(getPath());
}

此致便完成了忽略serialVersionUID不一致,导致无法反序列化读取的问题。

治本之法

上次解决方案只是忽略serialVersionUID不一致来读取,不过每次都要使用CompatibleInputStream来读取,便有些麻烦,其实我们可以在解析之后,再将其写入到文件或数据库中,这样便不用每次使用CompatibleInputStream来读取了。

修该旧序列化数据的serialVersionUID
/**
 * 修改序列化数据
 *
 * @return 反序列化之后的对象
 */
public static void updateObject(Path path) throws IOException, ClassNotFoundException {
    InputStream inputStream = Files.newInputStream(path);
    // 解析反序列化数据
    ObjectInputStream s = new CompatibleInputStream(inputStream);
    // 读取对象
    Object readObject = s.readObject();
    // 写入数据
    Path objPath = writeObject(readObject);
    // 读取数据
    Object obj = readObject(objPath);
    System.out.println(obj);
}
测试
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 输出结果为:User(userName=张三, age=18)
    updateObject(getPath());
}

至此便完成了修改serialVersionUID的功能,这样只需执行一次,另外我们添加了serialVersionUID 的值为1,下次无论是新增还是删除字段,都不会发生序列化的问题了,不过,最优的解决办法还是在创建类时就手动创建serialVersionUID 的值,也就不会遇到今天的问题了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值