Java序列化 – java.io.InvalidClassException本地类不兼容

我有一个公共类,实现Serializable,由其他多个类扩展。 只有那些以前的序列化的子类 – 从来没有超级类。

超类定义了一个serialVersionUID。

我不知道它是否重要,但它不是标记为私人的,而是它只是默认的保护 – 你可能会说它是包保护

static final long serialVersionUID = -7588980448693010399L; 

超类,也没有任何子类,但实现了readObject或writeObject,并且没有一个子类具有明确定义的serialVersionUID。 我想在超类中定义的就足够了。

尽pipe如此,直到一个新的实例variablesList / ArrayList和一个新的方法被添加到超类中,并且一些私有的实例variables被添加到了它的一个子类中。

现在当试图回读以前的序列化对象时,抛出了一个exception。 一个类似于这样的:

 com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196 

我假设这是因为默认的serialVersionUID,因为我没有在任何一个子类中声明,因为超类和一个子类的变化,现在已经改变了。

如何摆脱这种困境的build议将不胜感激。 我假设我需要实现readObject和writeObject,但除了调用defaultReadObject()和defaultWriteObject(),我不完全确定我需要做什么。 我也不知道是否需要将serialVerisonUID添加到所有子类,或者如果readObject和writeObject需要由每个子类实现,或者如果我可以只在一个超类中实现它们一次。

@DanielChapman给出了serialVersionUID的一个很好的解释,但没有解决scheme。 解决方法是:在所有类上运行serialver程序。 将这些serialVersionUID值放入当前版本的类中。 只要当前类与旧版本是串行兼容的,你应该没问题。 (注意将来的代码: 所有的 Serializable应该有一个serialVersionUID

如果新版本不是串行兼容的,那么你需要用一个自定义的readObject实现来实现一些魔法(如果你试图编写与旧代码兼容的类数据,你只需要一个自定义的writeObject )。 一般而言,添加或删除类字段不会导致类序列不兼容。 通常会改变现有领域的types。

当然,即使新类串行兼容的,你仍然可能需要一个定制的readObject实现。 你可能想要这个,如果你想填写从旧版本的类保存的数据中缺less的任何新字段(例如,你有一个新的列表字段,你想在加载旧的类数据时初始化为一个空列表)。

这里的简短答案是,如果不指定序列ID,则通过散列来计算序列ID。 (静态成员不是inheritance的 – 它们是静态的,只有(1)它属于这个类)。

http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html

getSerialVersionUID方法返回此类的serialVersionUID。 请参阅第4.6节“stream唯一标识符”。 如果未由类指定,则返回的值是使用由美国国家标准局定义的安全散列algorithm(SHA)从类的名称,接口,方法和字段计算出的散列值。

如果你改变一个类或它的层次结构,你的哈希将是不同的。 这是一件好事。 你的对象现在有不同的成员。 因此,如果你从序列化的forms读回来,它实际上是一个不同的对象 – 因此是例外。

长的答案是序列化是非常有用的,但可能不应该用于持久性,除非没有其他的方法来做到这一点。 这是一个危险的道路,具体是因为你正在经历什么。 你应该考虑一个数据库,XML,一个文件格式,可能是一个纯Java项目的JPA或其他持久性结构。

对于我,我忘了添加默认的序列号。

 private static final long serialVersionUID = 1L; 

这对我工作:

如果你将Serialized类对象写入一个文件,然后对文件进行一些修改并编译它,然后尝试读取一个对象,那么会发生这种情况。

所以,如果一个类被修改并重新编译,那么就把必要的对象写入文件。

PS:这不是一个解决scheme; 本来是一个解决方法。