我应该在异常子类中实现 serialVersionUID 吗?
Should I implement serialVersionUID in Exception Subclasses?
在一次代码审查中,我看到了这样的代码...
public class IntersectionException extends Exception
static final long serialVersionUID = 42L;
据我所知,我们需要实现 serialVersionUID 以确保我们反序列化与序列化对象相同的版本。如果我们看一下 class Exception 的层次结构,我们会看到有一个“Serialiazable”接口的实现。
但是在 class Exception 中我们已经有了一个 serialVersionUID。所以,我的问题是,我是否也应该在 Exception 的子 class 中声明 serialVersionUID?
这是一个愚蠢的 linting 规则,所以,不,你不应该这样做。
具体来说,规则:“所有可序列化的东西都必须有一个 serialVersionUID”是非常可疑的。将此规则应用于意外可序列化的事物(例如所有异常子类型)是彻头彻尾的愚蠢。
坚持 linting 规则 [A] 直接让你的代码变得更糟(是的,这条 linting 规则让你有动力让你的代码更糟。我用的是 'stupidity' 这个词有充分的理由),或者 [B] 只会让它变得稍微更糟但是像这样写 'semi-properly' 实际上非常昂贵,因为它需要花费大量时间才能正确完成,然后你的代码 仍然比你把它留在外面更糟糕。
我最好支持这些激烈的说法!
什么是 serialVersionUID
,甚至?
每当您使用 java 内置的序列化机制序列化任何对象时,class 的 'serial version ID' 也会写入数据。反序列化时,class 需要在 class 路径上,AND 它必须具有相同的 svUid。如果不是,则反序列化会抛出异常。任何 class 的 svUid 定义为:
static final long serialVersionUID
字段的值(如果存在),
如果不是,
- 计算它的结果,它基于对 class 的所有签名方面进行哈希处理(扩展,实现,所有字段的类型和名称,return 类型,名称,以及所有方法的参数类型和名称)。如果您想查看它,
serialver
工具(在任何 JDK 发行版的 bin
目录中)会告诉您。
为什么这个 linter 规则不好
抱怨这个是愚蠢的原因是序列化机制的 UID 部分就是这样,虽然它不是很好,但实际上更好这个 linting 规则将你推向什么。具体来说,这是串行版UID管理的正确模式1:
- 所有 classes 都以计算的 svUid 开始。 不要明确添加一个。
- 当你更新你的代码时,你实际上是在做一个愚蠢的举动,即使用序列化在同一应用程序的不同版本之间进行通信,那么默认情况下任何 class 不再与它自己的以前版本兼容如果任何签名的任何部分发生更改。
- ...除非作者明确选择编写与旧版本兼容的版本更新,并花时间编写测试并记录此行为。在这种情况下,他们显然拥有旧版本(如果您甚至没有旧版本,他们还能如何承诺您可以使用新版本反序列化旧版本的某些内容 'saved' ?) - 所以他们运行 旧版本的
serialver
工具和 将其显式写入新版本。
以上显然(无论如何对我来说)远远优于任何其他方法,if 版本之间的(反)序列化甚至是相关的(它真的不应该是) .因此,如果您遵循“根本不使用 java 的序列化机制”的规则,则应该禁用 linting 规则。如果您使用替代的最佳方法来解决它,仍然应该禁用 linting 规则:事实上,所有 classes 开始时都没有任何 svUid 是正确的。您的 linting 工具不知道这是需要与版本 1 序列化兼容的东西的版本 2,因此不会存在仅在那种(极其罕见)情况下触发的 linting 规则。因此,linting 规则不好,禁用它。
不行,必须加一个
好吧,让我们忽略所有这些并说我们必须为其添加一个值。但是我们应该输入什么数字呢?有3个选项。
常量值,例如1L
这只是改变了约定:您现在已将该行为更改为“任何签名更改,甚至添加字段,都无关紧要 - class 仍然与它的任何以前版本序列化兼容 - 要更改它,请添加 svUid'。
显然,这是一个糟糕的默认设置。但这是一种简单的方法,进一步火上浇油:如果你使用一些明显的常量(1、42、31 或诸如此类),linter 应该(如果有的话)发出警告。
实际的 svUid
如 serialver
会生成什么。这是多余的,因此违反了 DRY。功夫也不少:写个dummy,编译,运行serialver
,copy过来
你想要的:上一个版本的svUid,如果你兼容
这是正确的答案。不过,linter 工具无法帮助您。
注意:此答案是对项目 lombok 的功能请求的回复的稍微编辑的副本,该请求正是要求:您自己的异常子类型的 serialVersionUID。 See Project lombok issue #3016.
[1] 实际上,进行序列化的最佳方法是完全不使用 java 的内置机制,而是使用经过适当考虑的库。
在一次代码审查中,我看到了这样的代码...
public class IntersectionException extends Exception
static final long serialVersionUID = 42L;
据我所知,我们需要实现 serialVersionUID 以确保我们反序列化与序列化对象相同的版本。如果我们看一下 class Exception 的层次结构,我们会看到有一个“Serialiazable”接口的实现。
但是在 class Exception 中我们已经有了一个 serialVersionUID。所以,我的问题是,我是否也应该在 Exception 的子 class 中声明 serialVersionUID?
这是一个愚蠢的 linting 规则,所以,不,你不应该这样做。
具体来说,规则:“所有可序列化的东西都必须有一个 serialVersionUID”是非常可疑的。将此规则应用于意外可序列化的事物(例如所有异常子类型)是彻头彻尾的愚蠢。
坚持 linting 规则 [A] 直接让你的代码变得更糟(是的,这条 linting 规则让你有动力让你的代码更糟。我用的是 'stupidity' 这个词有充分的理由),或者 [B] 只会让它变得稍微更糟但是像这样写 'semi-properly' 实际上非常昂贵,因为它需要花费大量时间才能正确完成,然后你的代码 仍然比你把它留在外面更糟糕。
我最好支持这些激烈的说法!
什么是 serialVersionUID
,甚至?
每当您使用 java 内置的序列化机制序列化任何对象时,class 的 'serial version ID' 也会写入数据。反序列化时,class 需要在 class 路径上,AND 它必须具有相同的 svUid。如果不是,则反序列化会抛出异常。任何 class 的 svUid 定义为:
static final long serialVersionUID
字段的值(如果存在),
如果不是,
- 计算它的结果,它基于对 class 的所有签名方面进行哈希处理(扩展,实现,所有字段的类型和名称,return 类型,名称,以及所有方法的参数类型和名称)。如果您想查看它,
serialver
工具(在任何 JDK 发行版的bin
目录中)会告诉您。
为什么这个 linter 规则不好
抱怨这个是愚蠢的原因是序列化机制的 UID 部分就是这样,虽然它不是很好,但实际上更好这个 linting 规则将你推向什么。具体来说,这是串行版UID管理的正确模式1:
- 所有 classes 都以计算的 svUid 开始。 不要明确添加一个。
- 当你更新你的代码时,你实际上是在做一个愚蠢的举动,即使用序列化在同一应用程序的不同版本之间进行通信,那么默认情况下任何 class 不再与它自己的以前版本兼容如果任何签名的任何部分发生更改。
- ...除非作者明确选择编写与旧版本兼容的版本更新,并花时间编写测试并记录此行为。在这种情况下,他们显然拥有旧版本(如果您甚至没有旧版本,他们还能如何承诺您可以使用新版本反序列化旧版本的某些内容 'saved' ?) - 所以他们运行 旧版本的
serialver
工具和 将其显式写入新版本。
以上显然(无论如何对我来说)远远优于任何其他方法,if 版本之间的(反)序列化甚至是相关的(它真的不应该是) .因此,如果您遵循“根本不使用 java 的序列化机制”的规则,则应该禁用 linting 规则。如果您使用替代的最佳方法来解决它,仍然应该禁用 linting 规则:事实上,所有 classes 开始时都没有任何 svUid 是正确的。您的 linting 工具不知道这是需要与版本 1 序列化兼容的东西的版本 2,因此不会存在仅在那种(极其罕见)情况下触发的 linting 规则。因此,linting 规则不好,禁用它。
不行,必须加一个
好吧,让我们忽略所有这些并说我们必须为其添加一个值。但是我们应该输入什么数字呢?有3个选项。
常量值,例如1L
这只是改变了约定:您现在已将该行为更改为“任何签名更改,甚至添加字段,都无关紧要 - class 仍然与它的任何以前版本序列化兼容 - 要更改它,请添加 svUid'。
显然,这是一个糟糕的默认设置。但这是一种简单的方法,进一步火上浇油:如果你使用一些明显的常量(1、42、31 或诸如此类),linter 应该(如果有的话)发出警告。
实际的 svUid
如 serialver
会生成什么。这是多余的,因此违反了 DRY。功夫也不少:写个dummy,编译,运行serialver
,copy过来
你想要的:上一个版本的svUid,如果你兼容
这是正确的答案。不过,linter 工具无法帮助您。
注意:此答案是对项目 lombok 的功能请求的回复的稍微编辑的副本,该请求正是要求:您自己的异常子类型的 serialVersionUID。 See Project lombok issue #3016.
[1] 实际上,进行序列化的最佳方法是完全不使用 java 的内置机制,而是使用经过适当考虑的库。