使用Serializable接口没有必要的话,会有什么后果?

7
我需要为我的分布式系统课程开发一个Java RMI应用程序。
在讲座中,教授强调只有那些需要通过网络传递的类才实现Serializable接口。
这意味着让太多的类实现Serializable接口会有一些不利影响或代价。而那些不需要被发送到网络上的类则不必实现Serializable接口。
我不明白为什么会有任何不利影响,因为如果你从未将其发送到网络上,则永远不会发生序列化/反序列化。

1
我不理解这个问题。除了“不必要的开销”之外,惩罚会是什么?如果不打算通过网络发送任何内容,为什么还需要序列化?你的问题并不太有意义。 - user207421
1
@duffymo 项目的第一部分是开发RMI应用程序。第二部分是开发Java EE应用程序。第三部分是开发Google App Engine应用程序。本课程是分布式系统的介绍,因此介绍对象请求代理似乎是合理的。 - Thomas Vanhelden
1
@duffymo "过时"完全是观察者的主观看法。RMI/IIOP是J2EE的基础,有数百万个应用程序在使用它。我已将您的评论标记为不具建设性。 - user207421
1
我要先指出J2EE是1999年的术语,现在我们应该使用Java EE。事实上,有很多Java EE应用程序存在并不改变这个事实:世界已经从全Java应用程序中走过了。如果您的应用程序向客户端公开HTTP服务,则它将更加有用且寿命更长。这不仅仅是观察者的眼光问题,而是市场已经做出的决定。https://www.google.co.uk/trends/explore?q=javaee,web%20services - duffymo
1
@duffymo:我不同意你的概括。年龄并不重要;技术存在是为了解决问题。HTTP万能的新潮流对于高性能应用程序并不真正有效。我同意新应用程序可以从像二进制RPC协议这样的“新”东西中受益,但HTTP服务并不是RMI被使用的情况的直接替代品... - Sanjay T. Sharma
显示剩余9条评论
4个回答

8

只让需要通过网络传递的值实现Serializable接口。

您的教授建议您将使用Serializable最小化到仅在必要的领域。

这是因为序列化很容易泄露实现细节。实现Serializable接口表明了序列化的意图(即使对象从未被序列化),这意味着开发人员在修改这些类时应该小心,以避免破坏软件。


Joshua Bloch在他的书Effective Java中详细讲解了这个问题。

当你序列化一个对象时,它被实例化的类就不能再进行修改,除非进行特殊处理。如果你修改了这个类,那么二进制表示就不再与已序列化的对象匹配。因此,在修改类之前序列化的任何对象的反序列化都将失败。

如果一个类型实现了Serializable接口,它就有可能被序列化。如果该类型的实例已经被序列化,那么通过修改其实现可能会破坏代码。

由于没有简单的方法可以确定可序列化类型的实例是否已经被序列化(即使你可能不打算对对象进行序列化),所以开发人员在修改这些类型的实现时要非常谨慎。


- 这可以通过正确地对你的可序列化类型进行版本控制来避免,但由于可能会对没有契约更改的类型进行版本控制(没有编译时错误处理支持通知您),最好将显式版本控制最小化,以避免在设计中添加过多复杂性


这假设在实现Serializable接口时没有提供显式版本ID,这在我看来是必须的。你真的不想让自己处于VM实现生成版本ID的情况下。 - Sanjay T. Sharma
2
我认为这个答案值得肯定,尽管问题更多是元问题,比如说,你的应用程序使用了多少内存,这些问题更难以量化。 - nasukkin
1
@SanjayT.Sharma 这个答案也涉及到版本冲突,即开发人员在没有更改合同的情况下更改版本。使用Serializable会增加复杂性,而且没有编译时错误处理支持(不会因为版本实现更改而得到编译器错误),因此需要特殊处理以避免出现问题。在我看来,最好将这种特殊处理与要求封装起来,缩小潜在错误的范围。 - Dioxin

2

我认为教授所说的“只让需要通过网络进行值传递的类实现Serializable接口”有不同的理解。我的想法是,如果你必须以这种方式使用一个类,那么就要自己实现writeObject、readObject和readObjectNoData方法,这可能比默认实现更有效率。


原帖作者可能最能说明这种解释是否有可能。 - Ole V.V.

1

如果不必要地实现Serializable接口会有什么惩罚?

没有因为不必要地添加implements Serializable而受到惩罚。如果您从未对对象进行序列化,则不会发生任何事情。如果您确实对其进行序列化,则它可以正常工作而不会失败。这不是一种惩罚。

为什么教授强调这一点仍然是个谜。请问他。除了在序列化时,没有额外的开销。如果您通过RMI按值传递对象,您别无选择,只能实现Serializable接口,因此没有任何东西可以评估开销。这是毫无意义的。


是的,只有更少的运行时错误。但是你需要确保教授同意你的观点,因为有一些奇怪的想法在外面流传着。JVM从不查看“Serializable”:只有“ObjectOutputStream”才会这样做。 - user207421
@duffymo 关于“你已经过时”的评论,我真的不知道你在说什么,我也不喜欢这种毫无意义的言论。请停止这样的行为。问题是关于RMI的,我已经回答了。你的推断毫无根据。我也不需要你在RMI方面提供任何“预警”。 - user207421
我知道我在说什么。你已经回答了,足以让我投赞成票。我提供另一个视角,可能对学生和教授都有益。不要把它当成个人攻击或批评。 - duffymo
谢谢你的回答。问题是,我开始觉得我没有正确理解这个问题。正如我所说,这是可能的考试题库中一个经常出现的问题,由两部分组成。一些版本的问题有不同的第二部分:“当您使用Serializable而不是Remote时会发生什么?”这是一个明智的问题,我知道答案。 - Thomas Vanhelden
@duffymo 好的,你在说什么?“你已经过时了”这句话是针对谁说的?为什么? - user207421
显示剩余2条评论

0
如果有人想要后续子类化您的可序列化类,他们必须确保他们的类也是可序列化的。这可能需要他们不想做的努力。
比如说你有:
public class YourClass implements Serializable {
}

现在如果我写:

class MyClass extends YourClass {
    private SomeoneElsesClass myField;
}

SomeoneElsesClass不可序列化时,我的FindBugs将使用消息“可序列化类中的非瞬态非可序列化实例字段”标记myField。我试图使我的代码不受警告消息的影响,但在这种情况下,我无法避免。

我经常专门化Swing类。有时,我想拥有不可序列化类的实例字段,这基本上会在我的代码中引入错误。在现实生活中,我只收到一个警告消息,只要对象实际上没有被序列化,它就能正常运行;但这些警告消息仍然令我感到烦恼。


这个答案完全是错误的。已实现的接口是继承的,不需要额外操作。你根本不应该序列化Swing类,并且在每个Swing类的Javadoc中都有明确的警告。 - user207421
是的,已实现的接口是被继承的,这就是我所写的问题的原因。您是指以下警告吗?“警告:此类的序列化对象将与未来的 Swing 发布不兼容。当前的序列化支持适用于短期存储或在运行相同版本 Swing 的应用程序之间进行 RMI。从 1.4 开始,所有 JavaBeans™ 的长期存储都已添加到 java.beans 包中。请参阅 XMLEncoder。”这是否使我所写的任何内容无效?如何无效?这是否会删除我在 Eclipse 中看到的警告消息? - Ole V.V.
@EJP,仅仅因为实现的接口被继承,并不意味着序列化会正确工作,就像在父类上使用接口并不能保证可序列化性。 - shmosel
@shmosel 这正是它的意思,也是它所保证的。如果你有一些反例,请提供出来。 - user207421
@EJP 在建立合同的意义上,它可能会“保证”,但直到运行时才实际执行。您可以在包含非可序列化字段的类中实现或扩展Serializable,并且它将编译良好。 - shmosel

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