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

embedded/2024/10/25 14:34:20/

前言

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

复现过程

创建测试类
java">@Data
public class User implements Serializable {private String userName;private String age;}
写入序列化对象到文件
java">
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");
}
测试读取
java">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
java">@Data
public class User implements Serializable {// 添加serialVersionUID字段private static final long serialVersionUID = 1L;private String userName;private String age;}
测试读取
java">public static void main(String[] args) throws IOException, ClassNotFoundException {Object object = readObject(getPath());// 触发异常:local class incompatible: stream classdesc serialVersionUID = 7769644417338494813, local class serialVersionUID = 1System.out.println(object);
}
忽略serialVersionUID读取类信息
java">public class CompatibleInputStream extends ObjectInputStream {public CompatibleInputStream(InputStream in) throws IOException {super(in);}@Overrideprotected 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;}
}
编写新读取方法
java">/*** 获取序列化数据** @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);
}
测试新读取方法
java">public static void main(String[] args) throws IOException, ClassNotFoundException {// 输出 User(userName=张三, age=18)getObject(getPath());
}

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

治本之法

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

修该旧序列化数据的serialVersionUID
java">/*** 修改序列化数据** @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);
}
测试
java">public static void main(String[] args) throws IOException, ClassNotFoundException {// 输出结果为:User(userName=张三, age=18)updateObject(getPath());
}

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


http://www.ppmy.cn/embedded/10387.html

相关文章

QStringListModel

//内存中的数据(Data)QStringList list; //创建数据显示列表list.append("苹果");list.append("香蕉");list.append("桃子");//modelQStringListModel *listmodel new QStringListModel(list);//ViewQListView …

Mac 下最好用的播放器MPV

keywords: mpv Mac OS X 下最好用的播放器,没有之一。 快捷键 参考:mpv keybindings 按键功能RIGHT前进 5 秒LEFT后退 5 秒UP前进 60 秒DOWN后退 60 秒[0.9091 倍速播放]1.1 倍速播放{0.5 倍速播放}2.0 倍速播放Backspace还原到 1.0 倍速Space 或 p播…

Springboot+Vue项目-基于Java+MySQL的学科竞赛管理系统(附源码+演示视频+LW)

大家好!我是程序猿老A,感谢您阅读本文,欢迎一键三连哦。 💞当前专栏:Java毕业设计 精彩专栏推荐👇🏻👇🏻👇🏻 🎀 Python毕业设计 &…

Spring Boot 中单元测试框架 Mockito 的正确使用

Spring Boot 中单元测试框架 Mockito 的正确使用 本文是对前段时间修复老项目单元测试的总结记录,在修复过程中,发现中文互联网中的不少文章对单元测试框架Mockito的使用说明基本不准确又或是AI生成,希望我这篇能够把这方面简单说透说对。 文…

电脑便签记事本字体如何放大 便签记事本放大字体的方法

在日常工作中,电脑便签记事本已成为我记录琐事、安排日程的得力助手。然而,长时间盯着电脑屏幕,眼睛难免会感到疲劳。尤其是那些密密麻麻的小字,看久了真让人头疼。幸好,我发现了一个简单易行的方法,能轻松…

【linux外设挂载】linux系统找到U盘解决方案

linux系统怎样找到U盘? 精选 更新时间:2019-10-27 00:12 最满意答案 1. 用FDISK-l 来找,一般U盘会被LINUX认为SCSI设备,如果有SCSI设备那么U盘一般是最后一个SCSI设备.如果没有SCSI设备,一般sda就是U盘。 2. 若第一个插入的u盘一般是sda&…

5款制作表格的软件,一键帮你实现数据可视化

数据可视化是许多企业决定未来方向、产品研发和用户研究的关键。只有大量的数据支持才能做出最明智的决定,因此表格在可视化中逐渐发挥着不可替代的作用。可以看出,掌握表格制作技巧是多么重要。然而,不能制作表格的小型合作伙伴不必担心。国…

增强现实(AR)技术的应用场景

增强现实(AR)技术将虚拟信息与现实世界融合,为用户提供更加直观、交互式的体验。AR技术具有广泛的应用前景,可以应用于各行各业。以下是一些AR的应用场景。北京木奇移动技术有限公司,专业的软件外包开发公司&#xff0…