Redis 序列化和反序列化

12

我注意到我的一些存储在 Redis 中的序列化对象存在反序列化问题。

通常情况下,当我对存储在 Redis 中的对象类进行更改时,就会出现这种问题。

我想了解这个问题,以便我可以为解决方案设计一个清晰的方案。

我的问题是,什么会导致反序列化问题呢? 删除公共/私有属性会导致问题吗? 添加新属性呢?新增一个类中的函数是否会引起问题?多构造函数呢?

在我的序列化对象中,我有一个 Map 属性,如果我更改 myObject(例如更新一些属性、添加函数等),会导致反序列化问题吗?

1个回答

25

什么会导致反序列化问题?

在回答您的问题之前,我想先给您一些背景知识。

序列化运行时会为每个可序列化的类关联一个版本号,称为 serialVersionUID,在反序列化期间用于验证已序列化对象的发送方和接收方是否已加载了与序列化兼容的该对象的类。如果接收方已加载了一个具有与相应发送方类不同 serialVersionUID 的对象的类,则反序列化将导致 InvalidClassException 异常。

如果可序列化的类没有显式声明 serialVersionUID,则序列化运行时将为该类计算一个默认的 serialVersionUID 值,该值基于该类的各个方面。它使用以下类信息来计算 SerialVersionUID:

  1. 类名。
  2. 类修饰符作为 32 位整数编写。
  3. 按名称排序的每个接口的名称。
  4. 按字段名称排序的每个字段(除了 private static 和 private transient 字段):
  5. 字段名称。
  6. 字段修饰符作为 32 位整数编写。
  7. 字段描述符。
  8. 如果存在类初始化程序,请输出以下内容:

    方法的名称。

    方法的修饰符 java.lang.reflect.Modifier.STATIC,作为 32 位整数编写。

    方法的描述符 ()V。

  9. 按方法名称和签名排序的每个非私有构造函数:

    方法的名称。

    方法的修饰符作为 32 位整数编写。

    方法的描述符。

  10. 按方法名称和签名排序的每个非私有方法:

    方法名称。

    方法的修饰符作为 32 位整数编写。

    方法的描述符。

所以,回答您的问题:

删除公共/私有属性会导致问题吗?添加新属性可能吗?在类中添加新函数会创建问题吗?更多的构造函数呢?

是的,默认情况下,所有这些添加/删除都会导致问题。

但是,克服这个问题的一种方法是显式定义 SerialVersionUID,这将告诉序列化系统我知道该类会随时间演变(或已演变),并且不会抛出错误。因此,反序列化系统仅读取两侧都存在的字段并分配值。在反序列化侧新增的字段将获得默认值。如果在反序列化侧删除了某些字段,则算法只会读取并跳过。

以下是声明 SerialVersionUID 的方法:

private static final long serialVersionUID = 3487495895819393L;

非常感谢您提供这个信息丰富的答案。我还有一个问题,只是为了澄清一下。当您说“显式定义SerialVersionUID”时,将此UID定义为默认值“private static final long serialVersionUID = 1L;”是否可以? - Vic Cebedo
我的意思是,如果类没有声明UID,则会使用默认值。但是!如果我声明了UID,但值仍为默认值,它是否仍会计算UID? - Vic Cebedo
嗨,是的,只要两边的值相同,您可以定义任何值。顺便说一下,1L不是默认值,您可以将其视为版本号。 - Sathish
那么 serialVersionUID 本身应该是瞬态的吗?我想不是,但是在 Redis 中反复存储相同的值可能会占用大量内存? - Andreas Hartmann

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