Java反序列化,将字段更改为瞬态的

3

背景

我有一个类,它没有覆盖任何序列化特性,也没有serialVersionUID,但仍被序列化、存储和后续反序列化。这是一个配置对象,当更改配置时,数据实际上是从配置 UI 中读取的,然后对象通常地“从头开始”创建并序列化以进行存储。只有在使用时,对象才会通过反序列化创建。

现在,在这个类中添加了两个字段,这些字段不应该被序列化,但它们却被序列化了... 当这些字段在默认反序列化后保持为null时,会导致一些反序列化问题(NullPointerException),从而破坏类的不变性。解决方法是打开配置 UI 并保存配置,从而保存对象的正确序列化形式。

问题

现在,如果我以以下方式之一修改类来快速修复问题,那么从保存的配置数据中反序列化对象会发生什么情况呢:

  1. 移除这些字段,然后保存的数据是新版本,并包含这些字段吗?
  2. 将这些字段更改为transient,并保存的数据是新版本,并包含这些字段吗?
  3. 将这些字段更改为transient,并保存的数据是旧版本,并没有这些字段吗?

为了更具体,假设添加的字段是:

private final Map<String, String> extraProperties = new HashMap<String, String>(); 

这个字段要么从这个类中移除,要么更改为 private final transient 字段。

附注:无需告诉我,可能应该添加自定义序列化代码,然后将整个代码重构,将持久数据和临时数据分离到不同的类中...


1
你可以为新类添加一个serialVersionUID,使其与之前(隐式)版本相匹配。这在一些JDK类中已经完成,例如Vector,以保持与旧版本的向后兼容性。然后,你可以实现自定义反序列化逻辑。 - radai
如果要继续使用默认的序列化代码,您需要找出现有数据中使用的serialVersionUID,并将其添加到您的类中。 - Thilo
是的,我知道该怎么正确地解决它。也就是说,我知道需要重新阅读《Effective Java》中的哪一章节 ;)。但是我没有立即找到对我的具体问题的确切答案。 - hyde
好的,找到了一个非常相关的问题:https://dev59.com/x2865IYBdhLWcg3wKLJP - hyde
Java序列化在处理数据结构变更方面表现特别差。如果您需要支持此功能,我建议使用文本序列化之一,例如XStream或JSon或自己编写的序列化方式。 - Peter Lawrey
1个回答

2

移除(或使其短暂)您不想序列化的冗余字段。然后尝试反序列化加载旧版本,其中已经存在现在已删除的非瞬态字段。当然,这将导致错误,因为类 serialVersionUID 现在不同。但是,新旧两个 serialVersionUID 都应该包含在消息中。

现在只需在您的类中定义 private static long serialVersionUID =,将其设置为旧的先前值。文件中带有多余字段的类内容将被加载,并且将忽略这些多余字段的值。

但是,您现在可能有两种不同类型的保存文件:旧版本和新版本。这些将具有不同的 serialVersionUID,因此我们可以加载其中一个,但不能同时加载两个。serialVersionUID 是 final 的,但也许您仍然可以像这里描述的那样设置和测试不同的值。

从序列化的角度来看,将字段更改为瞬态与删除字段相同。它不会被存储,也不会被加载。但是,如果未固定,则将以前的非瞬态字段声明为瞬态将更改 serialVersionUID

如果按项检查,如果 serialVersionUID 现在是硬编码的 并且 与文件中的 serialVersionUID 匹配,则回答您的问题是:

  1. 无。
  2. 无。
  3. 无。

所谓“无”,是指类被反序列化而不对瞬态字段(如果存在)分配值,并且不报告错误。

如果 serialVersionUID 不匹配,则会抛出异常,即使类的其余部分匹配。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接