题 Java的序列化如何工作以及何时应该使用它而不是其他一些持久性技术?


我最近一直在努力学习更多,并且通常测试Java的工作和个人项目的序列化,我必须说我越了解它,我就越不喜欢它。这可能是由错误的信息造成的,所以这就是为什么我要问你们这两件事:

1: 在字节级别,序列化如何知道如何将序列化值与某些类匹配?

我在这里遇到的一个问题是我用ArrayList进行了一个小测试,其中包含值“one”,“two”,“three”。序列化后,字节数组需要78个字节,这对于如此少量的信息(19 + 3 + 3 + 4字节)来说似乎非常多。当然,这肯定会有一些开销,但这导致了我的第二个问题:

2: 序列化可以被认为是一种很好的持久化对象的方法吗?现在很明显,如果我使用一些自制的XML格式,持久性数据将是这样的

<object>
    <class="java.util.ArrayList">
    <!-- Object array inside Arraylist is called elementData -->
    <field name="elementData">
        <value>One</value>
        <value>Two</value>
        <value>Three</value>
    </field>
</object>

与XML一般来说,它有点膨胀,占用138个字节(没有空格,即)。在JSON中也是如此

{
    "java.util.ArrayList": {
        "elementData": [
            "one",
            "two",
            "three"
        ]
    }
}

这是75字节所以已经比Java的序列化略小。使用这些基于文本的格式,显然必须有一种方法可以将基本数据表示为文本,数字或两者的任意组合。

那么回顾一下,序列化如何在字节/位级别上工作,何时应该使用以及何时不应该使用序列化以及序列化的真正好处除了它是Java的标准之外?


36
2017-12-09 08:32


起源




答案:


我个人试图避免Java的“内置”序列化:

  • 它无法移植到其他平台
  • 它效率不高
  • 它很脆弱 - 让它来应对一个类的多个版本有点棘手。即使更改编译器也可能会破坏序列化,除非您小心。

有关实际字节含义的详细信息,请参阅 Java对象序列化规范

有各种替代方案,例如:

(免责声明:我在谷歌工作,而且我正在将C#的端口作为我的20%项目,所以我认为这是一项很好的技术:)

由于显而易见的原因,跨平台格式几乎总是比平台特定格式更具限制性 - 例如,协议缓冲区具有非常有限的本机类型集 - 但互操作性非常有用。您还需要考虑版本控制的影响,具有向后和向前兼容性等。文本格式通常是可手动编辑的,但在空间和时间方面往往效率较低。

基本上,您需要仔细查看您的要求。


45
2017-12-09 08:40



由于这以某种方式使它成为热门问题的方式,我想补充一下 Avro公司 到列表。 - TC1
@ TC1:完成,谢谢。 - Jon Skeet
另一种方法是将它存储在像SQLite这样的数据库中,特别是如果对象没有深度嵌套。 - Mechanical snail
我开始使用java序列化路径来查找一些我们需要持久保存的非常关键的文件,因为它很“简单”。只有它不是,因为我们有继承问题。我解决了这一切,但我不高兴我无法读取文件。然后我意识到,既然我们已经使用JSON作为我们的有线协议,我可以使用JSON进行序列化。这一切都在不改变所有数据类来实现Serializable和编写readObject()方法。无论如何,他们如何调用像readObject()和writeObject()这样的私有方法。这样快乐得多。 - Steve Cohen
@JonSkeet:请你详细说明这个标记界面的内部架构 什么时候我们应该去序列化 - Ved Prakash


序列化的主要优点是它非常易于使用,速度相对较快,并且保留了实际的Java对象网格。

但是你必须意识到它并不是真正用于存储数据,而主要是作为一种方式让不同的JVM实例使用RMI协议通过网络进行通信。


7
2017-12-09 09:18





请参阅Java对象 序列化流协议 用于描述文件格式用于序列化对象的语法。

就个人而言,我认为内置序列化可以保留短期数据(例如,将会话对象的状态存储到http请求之间),这在您的应用程序之外是不相关的。

对于具有更长实时时间或应在应用程序之外使用的数据,我会持久存储到数据库中或至少使用更常用的格式...


4
2017-12-09 08:47



我同意。它意味着通过电线传输对象,或者激活/钝化有用的东西,不是用于持久化对象,也不用于外部使用。 - Adeel Ansari


Java的内置序列化如何工作?

每当我们想要序列化一个对象时,我们就实现了java.io.Serializable接口。即使我们正在实现它,也没有任何实现方法的接口 向编译器或JVM指示内容 (作为。。而被知道 标记界面)。因此,如果JVM看到Class是Serializable,它会对这些类执行一些预处理操作。操作是,它添加了以下两个示例方法。

private void writeObject(java.io.ObjectOutputStream stream)
            throws IOException {
        stream.writeObject(name); // object property
        stream.writeObject(address); // object property
    }

    private void readObject(java.io.ObjectInputStream stream)
            throws IOException, ClassNotFoundException {
        name = (String) stream.readObject(); // object property
        address = (String) stream.readObject();// object property
    }

何时应该使用它而不是其他一些持久性技术?

内置 Serialization 当发送方和接收方都是Java时,它很有用。如果您想避免上述类型的问题,我们在框架的帮助下使用XML或JSON。


1
2018-01-06 00:54





一个月前我遇到了这种困境(见 我问的问题)。

我从中学到的主要教训是仅在必要时使用Java序列化,如果没有其他选项。就像Jon说的那样,它有它的垮台,而其他序列化技术则更容易,更快速,更便携。


0
2017-12-09 08:45





序列化意味着您将类中的结构化数据放入字节码的平面顺序中以保存它。

您通常应该使用除buildin java方法之外的其他技术,它只是开箱即用,但如果您将来在序列化类中有一些更改内容或更改订单,则会遇到麻烦,因为您无法加载他们正确。


0
2017-12-09 08:46





Java对象序列化(JOS)的优点是它可以正常工作。还有一些工具与JOS相同,但使用XML格式而不是二进制格式。

关于长度:JOS在开始时写一些类信息,而不是作为每个实例的一部分 - 例如完整的字段名称被记录一次,并且该名称列表的索引用于该类的实例。如果只编写一个类的实例,这会使输出更长,但如果编写几个(不同的)实例则更有效。我不清楚你的例子是否真的使用了一个类,但这就是为什么JOS比你期望的更长的一般原因。

顺便说一句:这是偶然的,但我不认为JSON会记录类名(正如您在示例中所做的那样),因此它可能无法满足您的需求。


0
2017-12-09 13:00





存储少量信息的原因是串行形式相对较大的原因是它存储有关串行化的对象类的信息。如果您存储列表的副本,那么您将看到该文件没有增长太多。存储相同的对象两次,差异很小。

重要的优点是:相对容易使用,非常快速并且可以发展(就像XML一样)。但是,数据相当不透明,它只是Java,将数据紧密地耦合到类,不受信任的数据很容易导致DoS。你应该考虑序列化的形式,而不仅仅是拍打 implements Serializable 到处。


0
2017-12-14 17:08