Java serialization - java.io.InvalidClassException local class incompatible

2022-08-31 15:37:58

我有一个公共类,它实现了Serializable,它由多个其他类扩展。以前只有那些子类被序列化过 - 从来没有超类。

超类定义了一个串行VersionUID。

我不确定它是否重要,但它没有被标记为私有,而只是具有默认保护 - 您可能会说它是受包保护的

static final long serialVersionUID = -7588980448693010399L;

然而,超类或任何子类都实现了readObject或writeObject,并且没有一个子类具有显式定义的serialVersionUID。我认为在超类中定义的一个就足够了。

尽管如此,就回读以前序列化的对象而言,一切都很好,直到一个新的实例变量List/ArrayList以及一个新方法被添加到超类中,并且一些私有实例变量被添加到其子类之一。

现在,当尝试读回以前序列化的对象时,会引发异常。一个类似于这样:

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

我假设这是因为默认的串行VersionUID,因为我没有在任何子类中声明一个而使用,现在由于超类和一个子类中的更改而已更改。

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


答案 1

@DanielChapman对串行VersionUID给出了很好的解释,但没有解决方案。解决方案是这样的:在所有类上运行串行器程序。将这些值放在类的当前版本中。只要当前类与旧版本串行兼容,就应该没问题。(注意未来的代码:你应该总是在所有类上都有)serialVersionUIDserialVersionUIDSerializable

如果新版本串行兼容,那么你需要用自定义实现做一些魔术(如果你试图编写与旧代码兼容的新类数据,你只需要一个自定义)。一般来说,添加或删除类字段不会使类序列不兼容。更改现有字段的类型通常会。readObjectwriteObject

当然,即使新类串行兼容的,您可能仍然需要自定义实现。如果要填写从旧版本的类保存的数据中缺少的任何新字段(例如,您有一个新的List字段,您希望在加载旧类数据时将其初始化为空列表),则可能需要此字段。readObject


答案 2

这里的简短答案是,如果您不指定串行ID,则通过哈希计算。(静态成员不是继承的 - 它们是静态的,只有(1)并且它属于类)。

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

The getSerialVersionUID method returns the serialVersionUID of this class. Refer to Section 4.6, "Stream Unique Identifiers." If not specified by the class, the value returned is a hash computed from the class's name, interfaces, methods, and fields using the Secure Hash Algorithm (SHA) as defined by the National Institute of Standards.

If you alter a class or its hierarchy your hash will be different. This is a good thing. Your objects are different now that they have different members. As such, if you read it back in from its serialized form it is in fact a different object--thus the exception.

The long answer is the serialization is extremely useful, but probably shouldn't be used for persistence unless there's no other way to do it. Its a dangerous path specifically because of what you're experiencing. You should consider a database, XML, a file format and probably a JPA or other persistence structure for a pure Java project.