【问题标题】:Java Seralization and DeseralizationJava序列化和反序列化
【发布时间】:2013-01-08 09:15:32
【问题描述】:

我有一个对象如下:

    public class Records implements java.io.Serializable{
        private int cId;
        private int pId;
        private int vlaue;
        private int tag;

        public Records(int c, int p, int v, int t){
                this.cId=c;
                this.pId=p;
                this.value=v;
                this.tag=t;
        }
}

我已经收集了大量数据,在上面的类中构造了对象并将它们序列化到磁盘。

我忘记包含在类文件中的一个转储是访问每个对象的值的方法。例如,访问特定对象的 cId 值。

我修改了类定义以添加此类方法,但后来我无法将对象反序列化回 Records 类并出现此运行时错误:

java.io.InvalidClassException: Records; local class incompatible: stream classdesc serialVersionUID = -1232612718276774474, local class serialVersionUID = -8244963718951538904
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:579)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1600)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1513)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1749)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1346)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
    at DeSerialise.main(DeSerialise.java:21)

我想我需要告诉java它们是相同的定义并修改serialVersionUID但不太确定如何?欢迎任何想法!

【问题讨论】:

标签: java serialization deserialization object-serialization


【解决方案1】:

尝试将以下内容添加到您的课程中:

private static final long serialVersionUID = -1232612718276774474L;

这将使您的类的 serialVersionUID 与实例序列化时使用的编译器生成的值保持一致。

documentation 的以下引用值得一读(强调我的):

序列化运行时将一个版本号与每个可序列化类相关联,称为 serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。如果接收者为对象加载了一个类,该对象的 serialVersionUID 与相应发送者的类不同,则反序列化将导致 InvalidClassException。可序列化的类可以通过声明一个名为“serialVersionUID”的字段来显式声明自己的serialVersionUID,该字段必须是静态的、最终的和long类型:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化类没有显式声明 serialVersionUID,则序列化运行时将根据类的各个方面为该类计算默认的 serialVersionUID 值,如 Java(TM) 对象序列化规范中所述。 但是,强烈建议所有可序列化的类都显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对可能因编译器实现而异的类细节高度敏感,因此可能会在反序列化期间导致意外的 InvalidClassExceptions。因此,为了保证不同 java 编译器实现的 serialVersionUID 值一致,可序列化类必须声明显式的 serialVersionUID 值。 还强烈建议显式的 serialVersionUID 声明尽可能使用 private 修饰符,因为此类声明仅适用于立即声明类--serialVersionUID 字段作为继承成员没有用。数组类不能显式声明 serialVersionUID,因此它们始终具有默认计算值,但数组类无需匹配 serialVersionUID 值。

【讨论】:

  • 感谢您的回答和指点。它有帮助,而且很管用。
【解决方案2】:

您可以在您的类中声明串行版本 ID,如下所示:

private static final long serialVersionUID = -1232612718276774474L;

【讨论】:

  • 添加随机的 serialVersionUID 无济于事。
  • @JBNizet 是对的。随机的 serialVersionUID 不起作用,甚至与上面答案中选择的一个 java 编译器不匹配。
  • 如果您不包含序列版 UID,Java 会为您生成一个。反序列化时,它会检查类的序列版本 UID 是否与存储在序列化字节中的 UID 匹配。因此,如果您想反序列化一个字节数组,您的类必须具有与 Java 在序列化对象时生成并写入字节数组的 UID 相同的 UID。当然,类的字段必须兼容。
  • 添加随机 id 无助于反序列化具有不同 id 的现有序列化对象,但它适用于添加该随机 id 后序列化的任何对象
  • @foampile 你可以省略它并从第一个异常中获取它,就像这里一样,或者你可以使用 serialver 工具。
【解决方案3】:

是的,为类指定 serialversionId 非常重要。否则很难识别序列化的类。

如果您这样做,您也可以创建新的序列化对象而不是使用旧的序列化对象。

Eclipse IDE 会自动生成 serialversionId 尝试使用。

【讨论】:

    【解决方案4】:

    问题是您序列化数据时没有在您的类中定义 serialVersionUID。添加 setter 和 getter 是可以的,并且不会影响序列化,但是当您重新编译该类时,编译器可能会生成另一个 UID,这会在您尝试反序列化数据时导致异常。如果需要实际反序列化您使用类的第一个版本序列化的数据,您需要知道编译器第一次生成的 UID:这可以在错误消息中看到,因此您需要将 serialVersionUID 设置为 -1232612718276774474L .

    【讨论】: