簡體   English   中英

Java反序列化向后兼容性

[英]Java de-serialization backward compatibility

如何反序列化在序列化后修改的類?

更具體地說,我知道當類的初始版本中具有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)     

請幫我解決一下這個。

感謝@GáborBakos。

這可以通過為較舊的類創建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 + "]";
}

}

工作正常。

更多信息請參考: serialver

第一個建議:使用序列化,因為一切都差不多了。

第二條建議:使用serialVersionUID並使用一個版本對其進行修復:在這里警告您並防止在不同序列化版本之間造成混淆。

因此:如果更改字段或字段的含義,請更改serialVersionUID。

然后您有向后兼容性問題。

請參閱以下內容以獲得許多想法: 管理序列化Java對象的多個版本

恕我直言:

  • 無論采用哪種解決方案,請記住您的程序將使用部分數據來管理對象:然后,您必須使用或不使用數據來管理所有案例。

  • 如果您不經常更改版本,請使用幾個不同的類。 也許是子類或接口的實現:然后,您可以獲取程序,並管理多個版本的對象:MyClass_V1,MyClass_V2等。反序列化時,可以測試/重試並獲得良好的Object。 之后,您可能必須在各個類之間轉換數據

  • 如果您更改版本,則通過添加新字段(而不更改舊字段),會稍微容易一些(子類,直接轉換為父級)

  • 或者您可以考慮使用XML結構進行序列化和反序列化:您可以具有向后和向前的兼容性,因為它是可擴展的 :字段存在或為空。 您必須自己管理映射或使用一些庫。

希望能幫助到你 !

我會記住以下幾點,

  1. 每個Serializable類都包含一個serialVersionUID (是否明確指定了一個都沒有關系)。
  2. 有兼容的更改,有不兼容的更改
    例如,添加新字段是兼容更改,刪除字段不是兼容更改。 添加/刪除/編輯方法通常是兼容的更改,但是在您的情況下,肯定不是這樣(添加toString()方法后更改了serialVersionUID

3,在修改類之前,可以使用serialver實用工具找到舊類的serialVersionUID並在新類中使用它

不要以為還有其他魔術:)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM