【问题标题】:Why Java needs Serializable interface?为什么 Java 需要 Serializable 接口?
【发布时间】:2010-10-01 06:28:27
【问题描述】:

我们大量使用序列化,并且必须在我们使用的每个对象上指定 Serializable 标记是一种负担。尤其是当它是一个我们无法真正改变的 3rd-party 类时。

问题是:既然 Serializable 是一个空接口,并且一旦添加 implements Serializable,Java 就会提供强大的序列化 - 为什么他们不让所有东西都可序列化,仅此而已?

我错过了什么?

【问题讨论】:

  • 如果你想让自己的对象可序列化怎么办?还是我误会了什么?
  • 我仍然会得到 NotSerializableException 因为我的对象的所有字段都必须是可序列化的
  • 完全同意 Pop Catalin 和 dmitry 的“这个 Serializable 技巧只是十年或两年前做出的另一个错误决定”。序列化有多危险并不重要。而且,既然声明是明确的,那么“你知道你必须特别注意”是不正确的:每个需要它的人都首先放置“实现”的东西,然后在出现问题时考虑暗示。如果他们给了我们一个“Unserializable”接口来应用在特殊情况下,那就更清楚了。

标签: java serialization


【解决方案1】:

序列化充满了陷阱。这种形式的自动序列化支持使类内部成为公共 API 的一部分(这就是 javadoc 为您提供persisted forms of classes 的原因)。

为了长期持久化,类必须能够解码这种形式,这限制了您可以对类设计进行的更改。这会破坏封装。

序列化也可能导致安全问题。通过能够序列化它所引用的任何对象,一个类可以访问它通常无法访问的数据(通过解析结果字节数据)。

还有其他问题,比如内部类的序列化形式没有很好的定义。

使所有类可序列化会加剧这些问题。查看Effective Java Second Edition,特别是第 74 条:明智地实现 Serializable

【讨论】:

  • 这显然是更好的答案,我很失望选择的答案不是这个,似乎发布者选择了“我很生气,因为我必须宣布事情可序列化”议程。这是一个人想要放飞自我而不注意过去已经学到的安全和设计课程的例子。
  • @McDowell 你所说的类的持久化形式是什么意思?我点击了那个链接,但不明白你的意思?你能解释一下吗?
  • @Geek - URL 类型的序列化形式(例如)定义了该类型必须具有哪些私有字段以及它们必须以什么顺序声明。
  • @McDowell 为什么顺序很重要?
  • @McDowell 嗨。我是 4 年前这个问题的原始发帖人,如果这意味着什么,我刚刚选择了你的答案作为接受。我认为您的答案确实更好,当时我可能还太不成熟,无法这样看。现在修复:)
【解决方案2】:

我认为这次 Java 和 .Net 的人都搞错了,最好让所有东西都默认可序列化,而只需要标记那些无法安全序列化的类。

例如,在 Smalltalk(一种 70 年代创建的语言)中,默认情况下每个对象都是可序列化的。我不知道为什么在 Java 中不是这种情况,考虑到绝大多数对象可以安全地序列化而只有少数对象不是这样的事实。

将对象标记为可序列化(带有接口)并不会神奇地使该对象可序列化,它一直是可序列化的,只是现在您表达了系统可以在他的自己的,所以我认为没有真正好的理由让序列化成为现在的样子。

我认为这要么是设计师做出的错误决定,要么是序列化是事后才想到的,或者该平台从未准备好在默认情况下对所有对象进行安全一致的序列化。

【讨论】:

  • 在设计和实现类时需要特别小心,以确保实例以合理的方式序列化。可序列化接口实际上的意思是:“我作为一名程序员,已经了解序列化的后果并允许 JVM 序列化”
  • @Rolf Rander:大多数时候你根本不需要关心,只需将类标记为可序列化。如果序列化在所有对象上默认开启,那么每个开发人员的心态也会有所不同,那么让类可序列化是一件很自然的事情......
  • 它会在良好的类设计列表中增加另一个关注点,例如确保您没有内存泄漏。尽管如此,好的类设计越来越要求你的类在可以序列化时是可序列化的。
  • @StaxMan,因为稍后意识到你需要序列化一个类而你不能,可能会非常昂贵。这是几乎不需要付出额外努力的情况之一。如果您正在编写一个库并且其他人会在没有源代码的情况下使用它,则尤其如此
  • 我完全同意这个答案。在许多语言中,默认情况下所有内容都是可序列化的。实际上与 JVM 相同,因为使用反射很容易访问任何类成员,无论它是私有的还是非私有的。这个Serializable技巧只是十年或两年前做出的又一个错误决定,也是处理纯java时的另一个烦恼,比如标准库中的集合和字符串处理中的一些缺陷。很高兴有 Kryo,但它是依赖项,需要先找到它。这就是内置序列化应该如何完成的。
【解决方案3】:

并非所有内容都可以真正序列化。以网络套接字连接为例。您可以序列化套接字对象的数据/状态,但活动连接的本质将会丢失。

【讨论】:

  • 这将是我的问题,如果我不够聪明,无法尝试序列化套接字,我会在调试过程中发现我的错误。但是,现在我完全无法使用 Java 序列化,因为 3rd 方类无缘无故地没有实现 Serializable。
  • 有一些不错的方法来处理这种情况,例如编写一个知道如何读写 3rd 方类的关键数据的 Serializable 包装类;使包装的实例瞬态并覆盖 writeObject 和 readObject。
  • 你能从你的api中继承所需的对象并序列化那些类吗?
  • @Joel:这是个好主意,但仍然是一个 hack。我想这整个事情只是另一个权衡错误。感谢您的 cmets。
  • 这是少数几个例子之一,它说明我们真正需要的是implements NotSerializable :)
【解决方案4】:

Serializable 在 Java 中的主要作用是在默认情况下实际上使所有其他对象不可序列化。序列化是一种非常危险的机制,尤其是在其默认实现中。因此,就像 C++ 中的友谊一样,默认情况下它是关闭的,即使使事情可序列化需要一点成本。

序列化增加了限制和潜在的问题,因为结构兼容性没有得到保证。默认关闭就好了。

我不得不承认,我见过很少有标准序列化可以满足我的要求的重要类。特别是在复杂数据结构的情况下。因此,您为使类可正确序列化而付出的努力使添加接口的成本相形见绌。

【讨论】:

    【解决方案5】:

    对于某些类,尤其是那些代表更物理的东西(如文件、套接字、线程或数据库连接)的类,序列化实例绝对没有意义。对于许多其他人来说,序列化可能是有问题的,因为它破坏了唯一性约束,或者只是迫使您处理您可能不想处理的不同版本的类的实例。

    可以说,默认情况下使所有内容都可序列化并通过关键字或标记接口使类不可序列化可能会更好 - 但是,那些应该使用该选项的人可能不会考虑它。就是这样,如果你需要实现 Serializable,你会被一个 Exception 告知。

    【讨论】:

      【解决方案6】:

      我认为这样做是为了确保作为程序员的你知道你的对象我是被序列化的。

      【讨论】:

        【解决方案7】:

        显然,在一些初步设计中,所有内容都是可序列化的,但出于安全性和正确性的考虑,最终设计最终如我们所知。

        来源:Why must classes implement Serializable in order to be written to an ObjectOutputStream?

        【讨论】:

          【解决方案8】:

          必须明确声明某个类的实例是可序列化的,语言迫使您考虑是否应该允许这样做。对于简单的值对象,序列化是微不足道的,但在更复杂的情况下,您需要真正考虑清楚。

          仅依靠 JVM 的标准序列化支持,您就会面临各种令人讨厌的版本控制问题。

          唯一性、对“真实”资源的引用、计时器和许多其他类型的工件都不是序列化的候选对象。

          【讨论】:

            【解决方案9】:

            阅读本文以了解可序列化接口以及为什么我们应该只创建少数类可序列化,并且我们应该注意在哪里使用瞬态关键字,以防我们想从存储过程中删除少数字段。

            http://www.codingeek.com/java/io/object-streams-serialization-deserialization-java-example-serializable-interface/

            【讨论】:

              【解决方案10】:

              嗯,我的回答是,这是没有充分理由的。从你的 cmets 我可以看出你已经学会了。其他语言愉快地尝试序列化所有在你数到 10 后不会跳到树上的东西。一个 Object 应该默认是可序列化的。

              所以,您基本上需要做的是自己阅读 3rd-party 类的所有属性。或者,如果您可以选择:反编译,将该死的关键字放在那里,然后重新编译。

              【讨论】:

              • 序列化增加了约束和潜在的问题,因为没有保证结构兼容性。恕我直言,默认情况下它是关闭的很好。
              • 我不确定你所说的“结构兼容性”是什么意思。
              【解决方案11】:

              Java 中有些东西根本无法做到 被序列化,因为它们是特定于运行时的。诸如流、线程、运行时之类的东西, 等等,甚至一些 GUI 类(连接到底层操作系统)也不能 被序列化。

              【讨论】:

                【解决方案12】:

                虽然我同意此处其他答案中提出的观点,但真正的问题在于反序列化:如果类定义发生更改,那么反序列化将存在真正的风险。永远不要修改现有字段是图书馆作者做出的一项重大承诺!维护 API 兼容性已经是一件苦差事了。

                【讨论】:

                • 这是不正确的。您需要阅读对象序列化规范的Versioning of Serializable Objects 章节,如果您在此处声明的内容属实,则该章节将不存在。在与先前的序列化不兼容之前,类定义可以在相当宽的范围内更改。这当然不是问题的答案。真正的原因与安全问题有关。
                • @EJP,为了真实起见,我编辑了我的答案以反映您的观点。尽管如此,这是一个很大的承诺,绝对应该选择加入而不是选择退出
                【解决方案13】:

                需要持久化到文件或其他媒体的类必须实现Serializable接口,以便JVM可以允许类对象被序列化。 为什么Object类没有序列化,那么没有一个类需要实现接口,毕竟JVM只有在我使用ObjectOutputStream时才会序列化类,这意味着控制权仍然在我手中让JVM序列化.

                Object 类默认不可序列化的原因在于类版本是主要问题。因此,每个对序列化感兴趣的类都必须明确标记为 Serializable 并提供版本号 serialVersionUID。

                如果 serialVersionUID 没有提供,那么我们在反序列化对象时会得到意想不到的结果,这就是如果 serialVersionUID 没有提供 JVM 抛出 InvalidClassException 的原因匹配。因此每个类都必须实现Serializable接口,并提供serialVersionUID来保证两端呈现的Class是一致的。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-11-10
                  • 2011-04-01
                  • 2016-12-29
                  • 2011-01-27
                  • 2019-04-25
                  • 1970-01-01
                  相关资源
                  最近更新 更多