使用serialVersionUID还是禁止显示警告?


70

我想创建一个类,例如,扩展HttpServlet?我的编译器警告我,我的类应该具有serialVersionUID。如果我知道该对象永远不会序列化,是否应该定义它或添加注释以禁止显示这些警告?

你会怎么做,为什么?


6
您认为servlet永远不会序列化是错误的。出于各种原因,允许servlet容器执行此操作。想想沉重的集群。
BalusC

您的IDE会警告您该类应具有serialVersionUID
user207421

1
@EJP不,编译器会警告您。
JOO ADAM

正如BalusC所说,某些Servlet容器默认将序列化Servlet会话。例如,Tomcat在关闭时会这样做。
罗勒·布尔克

Answers:


30

我不知道Java最佳实践,但是我想到如果您声称序列化将永远不会发生,则可以添加一个抛出的writeObject方法。然后,在可能无法应用警告的情况下,消除警告,确保安全。

否则,将来有人可能会通过父类对您的对象进行序列化,并最终得到默认的序列化形式,其中:

  • 表单在不同版本的代码之间不兼容。
  • 您已取消这种情况的警告。

添加一个ID听起来像是一个大障碍,因为您真正想要做的是不序列化。期望调用者不要序列化您的对象意味着您希望他们的HttpServlet是您的类时“知道”。拥有一个不可以序列化的Serializable对象,对多态性的破坏就在您的头上,并且您要做的至少是确保粗心的调用者知道它。


1
我认为这是很多开销,但是在专业环境中,这似乎很有必要。
Okami

如果您不打算支持序列化,则只想赞同确保禁止序列化(即有例外)的议案。
cynicalman

1
我不会遇到阻止序列化发生的麻烦。只是不要声明SVUID,这意味着每个编译都会生成一个唯一的SVUID。使序列化不可靠,但无意间破坏了依赖它的应用程序。
贾斯汀2010年

AFAIK默认UID由对象图生成,而不是通过编译生成。其他人在下面说了同样的话,我相信它在有效Java中是这样说的。
orbfish 2011年

3
@Justin Unreliable不是我想在与我的应用程序相同的句子中提及的单词。
Stefan 2012年

20

如果您不打算序列化实例,请添加一个SuppressWarning。

生成的序列ID可能有点危险。它建议您有意给它一个序列号,并保存下来进行序列化和反序列化。忘记更改类的应用程序的较新版本中的序列号很容易忘记。如果更改了类字段,则反序列化将失败。拥有SuppressWarning至少告诉您代码的读者您不打算序列化此类。


15

我拒绝被Eclipse吓倒而使代码混乱!

我只是将Eclipse配置为在缺少serialVersionUID时不生成警告。


4
我认为在不支持序列化时添加@suppressWarning并引发异常是专业环境中的最佳方法。要记住序列化的IDE在很多情况下可能很重要。
汤姆·布里托

1
我同意汤姆(Tom)的观点,在大多数情况下,这是令人讨厌且不必要的,但有时它是有用且重要的。如果您仍在项目中使用该方法,建议您将Project配置为这样做,而不是Eclipse本身(它将应用于每个项目)。使用@suppressWarning将消除烦人的情况,但是不会忽略“必要”的情况。
dominicbri7 2011年

12

感谢@ Steve Jessop对此的回答。这是5行代码...几乎没有麻烦。

@SuppressWarnings("serial")在有关课程的上方添加了内容。

我还添加了此方法:

private void writeObject(ObjectOutputStream oos) throws IOException {
   throw new IOException("This class is NOT serializable.");
}

希望那是史蒂夫的意思:)


UnsupportedOperationException也许更容易被接受,但是私有方法可能是不必要的
引发2012年

这是一个不错的尝试;不幸的是,它不会在编译时发出警告:(
Alex Cohn

6

即使您知道该对象将被序列化,也无需生成serialVersionUID,因为java会自动为您生成该对象并自动跟踪更改,因此您的序列化将始终正常进行。仅当您知道自己在做什么时才应生成它(向后序列化兼容性,手动更改跟踪等)。

因此,我想说在大多数情况下,禁止警告是最好和最安全的解决方案。


2
如果要序列化某些内容,则应始终提供一个明确的版本值(1、2、3,...)。这使您可以在兼容性破坏时告诉Java序列化。例如,如果您要做的只是添加字段,它仍然可以读取具有相同版本的旧.ser文件。
Scott Stanchfield's

3
@Scott即使在进行序列化时也不总是需要提供显式的版本值。它取决于序列化的范围以及无法反序列化的后果。如果只对由相同版本的代码序列化的对象进行反序列化(例如在Android上保存实例状态),那么自动生成的ID就可以了。同样,如果序列化的对象只是一个缓存,可以在序列化失败的情况下重建它,那也可以。手动ID会增加很多开发开销,因为您需要适当地处理反序列化中的版本偏斜。
Laurence Gonsalves

4

最好为实现可序列化的每个类生成SVUID 。原因很简单。您永远都不知道何时由您或某个第三方将其序列化。可以配置很多服务,这些服务将序列化servlet。对于存在的每个IDE,都会生成一个插件或仅使用模板并设置svuid = 1L。


我对此不太确定。如果添加SVUID,则很高兴看到要序列化的内容。如果客户端可以通过序列化整个程序来获取密码字段,则可能导致安全问题。
汤姆·布里托

在可序列化类中生成SVUID与可序列化字段无关。当然,您应该注意哪些字段已序列化。
拉斯蒂斯拉夫·科马拉

2
例如,如果生成的SVUID为1,则更改类的布局,现在这意味着您必须维护SVUID(并更新为2)。大多数人不会坚持这一点。如果您不打算将该类序列化,则这是不必要的。
贾斯汀2010年

1
“每个实现序列化的类的SVUID”如果类是可序列化的,那么世界上总是有人想要序列化它。总的来说,我的目标不是解决人们的懒惰。
拉斯蒂斯拉夫·科马拉

3

该警告使我发疯,因为每次将Swing类作为子类时,您都知道您永远不会序列化它,但是有一个愚蠢的警告。但是,是的,我让Eclipse生成了一个。


1
没错,更糟糕的是,您不能在包中添加禁止警告!
贾斯汀2010年

2

让Eclipse生成一个ID。快捷方便。警告不容忽视。如果您遇到了要对对象/ has /进行序列化的问题,这也省去了很多麻烦。


3
我不同意。在这种情况下,ID毫无用处。我认为应该禁止这种警告。
skaffman's

11
警告是提示您要求您做出明智的决定,而不是盲目遵循的说明。
McDowell

1
@McDowell序列化JavaDoc表示强烈建议使用。我的意思是,可以肯定,您可以说Sun / Oracle的家伙真是疯了,并且您比他们对Java的发展有更好的了解。但是,对原始问题(“ servlet可能已序列化”)的评论表明,有时(仅有时)开发人员假设知道会发生什么,但实际上却不知道。请参阅“ select()没有损坏”。
xmjx 2012年

1
我不确定是否可以免于麻烦。如果我更改类并且从不更改生成的ID,则我明确地告诉编译器/ JVM旧类仍然兼容。如果我只让运行时计算和ID,它至少有一定的机会检测到人类看不到的不兼容性。
marcus

@马库斯:你绝对正确。如果您在类中设置了1L一个硬编码的ID(无论是一个生成的ID,还是一个生成的ID ),那么如果您希望一切正常进行,那么每次兼容性发生更改时,您基本上都会承担更新该ID的负担。它们基本上像库版本,但在类级别。指定一个ID然后再不更改它,比不指定它有害得多。(更不用说抛出一个异常,writeObject这将成为我所预测的维护噩梦)。
Stijn de Witt

2

如果遗漏了serialVersionUID,则java将在编译时为该类生成一个(每次编译都会更改)。

反序列化对象时,将反序列化对象的serialVersionUID与jvm中的类进行比较。如果它们不同,则认为它们不兼容,并引发异常。例如,在升级程序并反序列化旧类之后,可能会发生这种情况。

我一直使用1L作为serialversionUID。它没有问题(与生成的默认值相比),并且仍然保留了以后通过增加id来破坏兼容性的选项。


7
串行版本UID成员不会在编译时添加到类中。如果缺少它,则在运行时根据类的成员签名进行计算。它仅在签名更改时更改,而不必在每次编译时更改。
erickson


2

这取决于。

如果使用不同的编译器多次编译源代码,则编译后的代码可能具有不同的serializationId,这些序列号会破坏序列化。然后,您需要在代码中显式地坚持一个常量serializationId。它必须是静态的,最终的,并且必须是每个类的(不可继承)。

但是,如果您始终使用特定的编译器编译代码,并且始终将代码一次性部署到所有VM,则可能需要进行严格的版本检查,并希望确保在任何时候都只运行一个版本的代码。在这种情况下,您应该禁止显示警告。因此,如果虚拟机未成功部署且正在运行旧版本的代码,则您可能会期望序列化过程中发生异常,而不是怪异的反序列化对象。碰巧是我的情况,我们过去有一个非常大的集群,我们需要严格的版本检查以发现任何部署问题。

无论如何,您应该尽可能避免序列化,因为与协议缓冲区或节俭相比,默认序列化非常慢,并且不支持跨语言互操作性。


1

如果您知道应用程序从不对事物进行序列化,请在应用程序范围内取消警告。这可以使用javac命令行参数来完成:

javac -Xlint -Xlint:-serial *******

这样,您将获得除“序列”之外的所有警告。IDE和Maven / SBT / Gradle之类的构建工具可以很好地工作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.