我想编写代码,即使类被更改(但你有旧的类版本),也可以反序列化类。
这个想法很简单。
serialVersionUID
serialVersionUID
是否等于当前班级版本的serialVersionUID
。ClassLoader
并将旧的类版本加载到工作区中。我想用这样的东西:
FileInputStream file = new FileInputStream(filename);
ObjectInputStream o = new ObjectInputStream(file) {
protected Class<?> resolveClass(java.io.ObjectStreamClass desc) throws IOException, ClassNotFoundException {
//5639490496474438904L is the suid of the older version
if (desc.getSerialVersionUID() == 5639490496474438904L) {
Class c01 = null;
URL eineURL = new URL("file://localhost/U:/MyJavaProj/bin/test/oldversion/");
URL[] reiURL = new URL[] {eineURL};
URLClassLoader clazLader = new URLClassLoader(reiURL);
c01 = clazLader.loadClass("test.SerObj");
return c01;
}
return super.resolveClass(desc);
}
};
SerObj obj = (SerObj) o.readObject();
问题出在最后一行。我当前的班级版本位于 U:/MyJavaProj/bin/test/SerObj.class 我的旧班级版本放在 U:/MyJavaProj/bin/test/oldversion/test/SerObj.class
在最后一行中,我阅读了旧的类版本,但将其转换为新版本:(
有没有人有想法或maby任何其他方法为Java中的序列化添加版本控制支持?
答案 0 :(得分:3)
我不知道您是否尝试修复现有问题或编写新功能。如果是后者,那么我会研究使用java序列化的高级功能。 java序列化支持各种工具,可以在一个类加载器中处理同一个类的多个串行版本(但是,关键是,你需要保持serialVersionUID与所有实例的相同)。
这是处理“不兼容”更改的示例,其中整数值更改为String值:
public class MyClass {
private static final int CUR_VERSION = 5;
private String _value;
private void readObject(ObjectInputStream in) {
// first value is always the serial version:
int dataVersion = in.readInt();
if(dataVersion == CUR_VERSION) {
// _value is a String
_value = in.readString();
} else {
// in older versions, _value was an int
_value = String.valueOf(in.readInt());
}
}
private void writeObject(ObjectOutputStream out) {
// always write version first
out.writeInt(CUR_VERSION);
out.writeString(_value);
}
}
答案 1 :(得分:2)
对此的简单回答是不要更改serialVersionUID
。
如果您认为自己对类进行了序列化不兼容的更改,请首先阅读对象序列化规范的Object Versioning部分,仔细检查一下,如您可能没有,如果您真的拥有,更改/添加writeObject
/ readObject
方法,以便他们可以应对旧的序列化格式。
当然不要试图在运行时使用该类的两个版本。它不起作用。
答案 2 :(得分:1)
你将无法在最后一行执行强制转换,因为它是一个不同的类(不可转换) - 你可能会收到一个令人困惑的消息,例如test.SerObj is not an instance of test.SerObj
。
这是因为一个类本质上是一个java.lang.Class
实例,其中在给定的类加载器中是唯一的。即使您引用了完全相同的*.class
文件,默认类加载器加载的SerObj
实例也与SerObj
加载的clazLader
实例不同。这两个课程都不能投入到另一个课程中。
因此,如果需要使用多个类加载器进行反序列化,则该方法无法返回SerObj
。 (除此之外 - 当两个类文件有任意差异时,怎么可能呢?)
一种可能的解决方法是定义一个接口,该接口定义在类的版本之间修复的行为。如果您设置临时类加载器,使其仅从oldversion类文件中加载SerObj
,并且它具有正确的父类加载器,那么您的旧SerObj
仍应实现根类加载器定义的接口类。
在这种情况下,新的SerObj
和旧SerObj
实例都可以转换为SerObjIface
接口,因此您可以声明您的方法返回那个。事实上,如果你想让调用者处理不同但类似的类(这是“同一类”的不同版本),你应该可以为此返回一个接口。