前言
开发过程中某个类并没有指定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 的值,也就不会遇到今天的问题了。