Java反序列化向后兼容性

Java反序列化向后兼容性,java,serialization,deserialization,Java,Serialization,Deserialization,如何反序列化序列化序列化后修改的类 更具体地说,我知道当一个类的初始版本中有serialVersionUID时,就可以做到这一点。对于没有serialVersionUID的类,有什么方法可以这样做吗 我有一个目标 package com.test.serialize; import java.io.Serializable; public class MyObject implements Serializable{ String name; public String

如何反序列化序列化序列化后修改的类

更具体地说,我知道当一个类的初始版本中有
serialVersionUID
时,就可以做到这一点。对于没有
serialVersionUID
的类,有什么方法可以这样做吗

我有一个目标

package com.test.serialize;

import java.io.Serializable;

public class MyObject implements Serializable{

    String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
我像这样序列化类

package com.test.serialize;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializeTest {

    public static void main(String[] args) {

        try {
            MyObject myObject = new MyObject();
            myObject.setName("Ajit");

            ObjectOutputStream objectOStr = null;
            ByteArrayOutputStream byteOStr = null;
            try {
                byteOStr = new ByteArrayOutputStream();
                objectOStr = new ObjectOutputStream(byteOStr);
                objectOStr.writeObject(myObject);

            } catch (IOException e) {
                System.out.println(e);
            } finally {
                try {
                    if (objectOStr != null)
                        objectOStr.close();
                } catch (IOException e2) {
                }
            }
            FileOutputStream fo = new FileOutputStream(new File("serialize"));
            fo.write(byteOStr.toByteArray());
            fo.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}
并像这样反序列化

package com.test.serialize;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.RandomAccessFile;

public class DeserializeTest {

    public static void main(String[] args) {

        try {

    //          File f = new File("serialize");
    //          FileInputStream fs = new FileInputStream(f);
            RandomAccessFile raF = new RandomAccessFile("serialize", "r");
            byte[] b = new byte[(int)raF.length()];
            raF.read(b);

            ObjectInputStream oIstream = null;
            ByteArrayInputStream bIstream = null;

            bIstream = new ByteArrayInputStream(b);
            oIstream = new ObjectInputStream(bIstream);
            Object finalResult = oIstream.readObject();
            System.out.println(finalResult.toString());
        } catch (IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}
过了一段时间,我补充道

@Override
public String toString() {
    return "MyObject [name=" + name + ", names=" + names + "]";
}
MyObject
。在添加之后,我得到了如下异常

java.io.InvalidClassException: com.test.serialize.MyObject; local class in 
compatible: stream classdesc serialVersionUID = 5512234731442983181, local class
serialVersionUID = -6186454222601982895
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:617)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1622)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1517)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1771)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
at com.test.serialize.DeserializeTest.main(DeserializeTest.java:25)     

请帮我解决这个问题。

我会记住以下几点

  • 每个
    Serializable
    类都包含一个
    serialVersionUID
    (无论您是否明确指定了一个)
  • 有兼容的更改,也有不兼容的更改
    e、 g.添加新字段是兼容更改,删除字段不是兼容更改。添加/删除/编辑方法通常是兼容的更改,但在您的情况下肯定不是这样的(
    serialVersionUID
    在添加
    toString()
    方法后发生了更改)
  • 3.在修改类之前,可以使用serialver实用程序查找旧类的
    serialVersionUID
    ,并在新类中使用


    不要认为还有其他魔术:)

    第一条建议:使用序列化,因为一切都差不多完成了

    第二个建议:使用serialVersionUID并用一个版本进行修复:它在这里警告您并防止不同序列化版本之间的混淆

    因此:如果更改字段或字段的含义,请更改serialVersionUID

    那么您就有了向后兼容性问题

    请参阅以下内容了解许多想法:

    IMHO:

    • 无论您采取何种解决方案,请记住,您的程序将管理包含部分数据的对象:然后您必须管理包含或不包含数据的所有情况

    • 如果你不经常改变你的版本:使用几个不同的类。可能是子类或接口的实现:然后您可以获得您的程序,并管理对象的多个版本:MyClass_V1、MyClass_V2等。当您反序列化时,您可以测试/重试并获得好的对象。之后,您可能需要在类之间转换数据

    • 如果您通过添加新字段(而不是更改旧字段)来更改版本,则更容易一些(子类,直接转换到父类)

    • >P>或者您可以考虑使用XML结构来序列化和反序列化:您可以具有向后和向前兼容性,因为它是“强>可扩展< /强>:字段在那里,或者是NULL。您必须自己管理映射或使用一些库

    希望有帮助

    谢谢@Gábor Bakos

    这可以通过为旧类创建serialVersionUID(这些签名应该与序列化期间的签名相同)并在当前类中添加serialVersionUID来解决

    serialver -classpath /***PATH***/bin com.test.serialize.MyObject
    
    返回

    com.test.serialize.MyObject:    static final long serialVersionUID = 5512234731442983181L;
    
    之后,我将其添加到我的MyObject中,如下所示

    package com.test.serialize;
    
    import java.io.Serializable;
    
    public class MyObject implements Serializable{
    
        /**
         * Added serial version Id of old class, created before adding new fields
         */
        private static final long serialVersionUID = 5512234731442983181L;
    
    
    public MyObject() {
        System.out.println("Constructor");
    }
    
    String name;
    
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    
    String names ="Altered after change!";
    
    public String getNames() {
        return names;
    }
    
    public void setNames(String names) {
        System.out.println("Setting names");
        this.names = names;
    }
    
    @Override
    public String toString() {
        return "MyObject [name=" + name + ", names=" + names + "]";
    }
    
    }
    
    它很好用


    更多信息请参考:

    您应该将新版本的
    SerialVerionId
    设置为旧版本的
    readObject
    /
    writeObject
    方法。如果启用所有编译器警告并注意它们,编译器将告诉您,这将发生,因为您没有提供serialVersionUID字段。删除字段不会阻止类被反序列化。对象被正确读入,但流中存在但不在类中的任何字段基本上都被丢弃。类中存在但不在流中的任何字段都被设置为默认值(null/0)。使用每个新的类版本更改serialVersionUID是一个城市神话,显然从来没有这样做过的人都在胡思乱想。如果你真的这样做了,你会立即有一个不可操作的系统。根本不改变它的想法,并管理类的更改,使其保持在对象序列化规范.javadoc的版本控制章节中指定的范围内,这也是某种城市神话:一个serialVersionUID,在反序列化期间用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。“那么兼容性=您可以保留版本号。不兼容=改变它。有了它,版本管理就如人们所希望的那样健壮。另一个神奇的、狡猾的、有自我意识的管理当然很有趣,但我不明白重新发明这个轮子的好处,也不确定它的连续性。。。