不必要地实现 Serializable 的惩罚是啥?

Posted

技术标签:

【中文标题】不必要地实现 Serializable 的惩罚是啥?【英文标题】:What is the penalty for unnecessarily implementing Serializable?不必要地实现 Serializable 的惩罚是什么? 【发布时间】:2016-12-29 00:40:41 【问题描述】:

我需要为我的分布式系统类开发一个 Java RMI 应用程序。

在讲座中,教授强调只让类实现必须通过网络按值传递的Serializable

这意味着让太多类实现Serializable会带来一些不利或不利影响。不需要通过网络发送的类。

我看不出有什么不利之处,因为如果您从未真正通过网络发送序列化/反序列化,就永远不会发生。

【问题讨论】:

我不明白这个问题。除了“不必要的开销”之外,还有什么惩罚其他?如果不通过网络发送,为什么还要序列化?你的问题没有多大意义。 @duffymo 项目的第一部分是开发一个 RMI 应用程序。第二部分是开发 Java EE 应用程序。第三部分是开发一个 Google App Engine 应用程序。该课程是对分布式系统的介绍,因此对对象请求代理的介绍似乎是合理的。 @duffymo '早已过时'完全在旁观者的眼中。 RMI/IIOP 是 J2EE 的基础,有数以百万计的应用程序在使用它。我已将您的 cmets 标记为不具建设性。 我将首先指出 J2EE 是 1999 年的术语;我们现在应该使用 Java EE。有很多 Java EE 应用程序的事实并没有改变世界已经从全 Java 应用程序转移的事实。如果将 HTTP 服务暴露给客户端,您的应用程序将更有用且寿命更长。它不仅仅是旁观者的眼睛。这是市场决定的。 google.co.uk/trends/explore?q=javaee,web%20services @duffymo:我不同意你的概括。年龄无关紧要;存在解决问题的技术。万事万物的 HTTP 新时尚并不真正适用于高性能应用程序。我同意新应用程序可以从二进制 RPC 协议等“更新”的东西中受益,但 HTTP 服务并不能真正直接替代使用 RMI 的情况...... 【参考方案1】:

只让类实现必须通过网络按值传递的 Serializable。

您的教授建议您将Serializable 的使用减少到严格需要的区域。

这是因为序列化是leaking implementation 的有力候选者。实现Serializable 表明了序列化的意图(即使对象实际上从未被序列化),这表明开发人员在修改这些类时应该小心以避免破坏软件。


Joshua Bloch 在他的书 Effective Java 中对此进行了介绍。

当您序列化一个对象时,如果不进行特殊处理,则不能再修改它实例化的类。如果修改类,二进制表示将不再匹配已序列化的对象。因此,在修改类之前序列化的任何对象的反序列化都会失败。

如果一个类型实现了Serializable,它就有潜力被序列化。如果该类型的实例被序列化,您可以通过修改其实现来破坏代码。

由于没有简单的方法可以确定可序列化类型的实例是否已序列化(尽管您可能不打算序列化对象),因此开发人员在修改这些类型的实现时要格外小心。


- 这可以通过properly versioning 您的可序列化类型来避免,但由于可能对没有合同更改的类型进行版本控制(没有编译时错误处理支持通知您) ,最好尽量减少显式版本控制以避免将excess complexity 添加到您的设计中。

【讨论】:

这假定在实现 IMO 必须的 Serializable 接口时没有提供 explicit 版本 ID。您真的不想让自己任由虚拟机实现为您生成它。 我认为这个答案值得称赞,尽管这个问题更像是一个元问题,并且比你的应用程序使用了多少内存更难量化。 @SanjayT.Sharma 答案还与versioning conflicts 相关,开发人员在不更改合同的情况下更改版本。使用Serializable 会增加不支持编译时错误处理的复杂性(不会因为版本控制 impl 更改而出现编译器错误),因此需要特殊处理以避免崩溃。 IMO,最好将这种特殊处理与需求一起封装起来,缩小潜在错误的范围。【参考方案2】:

不必要地实现 Serializable 的惩罚是什么?

不必要地实施Serializable 不会受到惩罚。如果您从不序列化对象,则不会因为您添加了implements Serializable 而发生任何事情。如果你对它进行序列化,它会起作用而不是失败。这不是惩罚。

为什么教授强调这是一个谜。问他。除了序列化时没有其他开销,如果您通过 RMI 按值传递对象,您别无选择来实现Serializable,因此没有什么可以评估开销的.毫无意义。

【讨论】:

这就是我最初的想法。浏览该课程可能的考试问题档案,我多次遇到这个问题。该问题要求解释远程、本地和可序列化之间的区别。第二部分询问如果你让太多的类实现 Serializable 会发生什么。我猜答案应该是“什么都没有”,对吧? 是的。只是更少的运行时错误。但是,您需要确定教授。同意你的观点,那里有一些奇怪的想法。 JVM 从不查看Serializable:只有ObjectOutputStream 这样做。 @duffymo Re '你已经过时了' 我真的不知道你在说什么,我不接受这种毫无意义的评论。请把它剪掉。这个问题是关于 RMI 的,我已经回答了。你的推断毫无根据。我也不需要你提供任何关于 RMI 的“提示”。 我知道我在说什么。你已经回答了;足以让我投票赞成。我正在提供另一种观点,学生和教授可能会从中受益。不要把它看成是个人,甚至是批评。 感谢您的回答。问题是,我开始认为我没有正确解释这个问题。正如我所说,它是可能的考试问题档案中重复出现的问题,它由两部分组成。该问题的某些版本具有不同的第二部分:“当您使用 Serializable 而不是 Remote 时会发生什么?”。这是一个明智的问题,我知道答案。【参考方案3】:

我会以不同的方式理解教授的“只让类实现必须通过网络按值传递的可序列化”。我认为这个想法是,如果你必须以这种方式使用一个类,那么你自己实现 writeObject、readObject 和 readObjectNoData,这可能比默认实现更有效。

【讨论】:

原始发帖人可能最能说明这种解释是否可能。【参考方案4】:

如果以后有人想继承你的可序列化类,他们必须确保他们的类确实也是可序列化的。这可能需要他们不想做的努力。

假设你有:

public class YourClass implements Serializable 

现在如果我写:

class MyClass extends YourClass 
    private SomeoneElsesClass myField;

并且SomeoneElsesClass不可可序列化的,我的 FindBugs 将标记myField 并带有消息“可序列化类中的非瞬态不可序列化实例字段”。我试图让我的代码免受警告消息的影响,但在这种情况下,我不能。

我经常专攻 Swing 课程。有时我想拥有不可序列化类的实例字段,这基本上会在我的代码中引入错误。在现实生活中,我只会收到一条警告消息,只要对象实际上没有被序列化,它就可以工作;我仍然对这些警告信息感到恼火。

【讨论】:

这个答案完全不正确。实现的接口是继承的。没有什么额外的事情要做。您根本不应该序列化 Swing 类,并且在每个类的 Javadoc 中都有针对它的特定警告。 是的,实现的接口是继承的,这就是造成我写的麻烦的原因。您指的是以下警告吗? “警告:此类的序列化对象将与未来的 Swing 版本不兼容。当前的序列化支持适用于运行相同版本 Swing 的应用程序之间的短期存储或 RMI。从 1.4 开始,java.beans 包中添加了对所有 JavaBeans™ 长期存储的支持。请参阅 XMLEncoder。”这会使我写的任何内容无效吗?如何?它是否删除了我在 Eclipse 中看到的警告消息? @EJP 仅仅因为实现的接口是继承的,并不意味着序列化会正常工作,就像在父类上使用接口并不能保证可序列化一样。 @shmosel 这正是它的意思,也正是它所保证的。如果您有一些反例,请提供它.. @EJP 它可能在建立合约的意义上“保证”它,但直到运行时才真正执行。您可以在包含不可序列化字段的类中实现或扩展Serializable,它会编译得很好。

以上是关于不必要地实现 Serializable 的惩罚是啥?的主要内容,如果未能解决你的问题,请参考以下文章

值不必要地四舍五入

iOS 中的 Bar 部分被不必要地扩展

改变的 GradientDrawable 在不同的地方被不必要地重复使用

数组(结构类型)的 Where 是不是已优化以避免不必要地复制结构值?

CMake - 不必要地构建两次对象?

为啥 vue3 不必要地重新渲染 v-for 中的节点?