我想编写代码,即使更改了类,该代码也可以反序列化类(但是您使用的是旧类版本)。

这个想法很简单。


阅读序列化类的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

在最后一行中,我阅读了旧的类版本,但将其转换为新的版本:(

是否有人对Java的序列化添加了版本控制支持?

最佳答案

您将无法在最后一行执行转换,因为它属于不同的类(不可转换)-您可能会收到诸如test.SerObj is not an instance of test.SerObj之类的令人困惑的消息。

这是由于一个类本质上是一个java.lang.Class实例,该实例在给定的类加载器中至关重要,因此是唯一的。即使您引用了完全相同的*.class文件,默认类加载器加载的SerObj实例也与SerObj加载的clazLader实例是不同的类。任何一个类别都不能转换为另一个类别。

因此,如果您需要使用多个类加载器进行反序列化,则该方法将不可能返回SerObj。 (此外-当两个类文件可能具有任意差异时,怎么可能?)



一种可能的解决方法是定义一个接口,该接口定义在类的版本之间固定的行为。如果设置临时类加载器,使其只能从oldversion类文件中加载SerObj,并且具有正确的父类加载器,则旧的SerObj仍应实现根类加载器定义的接口类。

在这种情况下,新的SerObj和旧的SerObj实例都可以转换为SerObjIface接口,因此您可以声明方法以返回该接口。实际上,如果您希望调用者处理不同而又相似的类(这是“同一类”的不同版本),则可以说应该为此返回一个接口。

09-05 13:43