【问题标题】:What's the difference between Serialization and simply store the object on disk?序列化和简单地将对象存储在磁盘上有什么区别?
【发布时间】:2012-07-24 02:23:35
【问题描述】:

我对此感到困惑。 因为在实现 Serializable 类时,我们需要使用 FileOutputStreamObjectOutputStream 之类的类。那我们为什么不直接使用这些类来做一些事情,比如将对象输出到文件,从文件中输入对象来直接维护对象的状态呢?为什么要先实现 Serializable 再做同样的事情?

【问题讨论】:

  • 如何在不序列化的情况下“将对象输出到文件”?
  • 想想当类的定义在保存和加载之间发生变化时的情况。更何况不是所有的类都可以保存在磁盘上。
  • @thinksteep 非常有用的信息!谢谢:)

标签: java serialization serializable fileoutputstream objectoutputstream


【解决方案1】:

这样理解……

Serializable 是标记接口,它表示您的类的对象可以转换为字节流,并在需要时最终返回为 java 对象。最初你可能认为每个类都应该是可序列化的,但这是不正确的考虑

Input- 和 OutputStreams 有一些文件句柄可供读取。当流变得不可用时,该文件句柄将关闭。所以这种情况下的序列化没有意义;并且反序列化永远不会恢复该句柄。

所以这应该回答为什么需要标记为可序列化?

现在实现定义对象应该如何写入或读取的方法;这应该由您定义,因此您需要所有这些流对象和 readObject、writeObject 方法。希望这能让您对这个概念有更多的了解。

【讨论】:

    【解决方案2】:

    Serializable 只是一个标记接口,这意味着它仅用于向实际执行序列化的代码发出信号,您(程序员)知道(或希望:-))此类可以在没有序列化的情况下进行序列化问题。

    序列化本身就是将对象转换为可以存储或传输的东西 - 即作为字节流。您可以将序列化与冷冻干燥进行比较,冷冻干燥是用于制造雀巢咖啡的技术——所有的水都被去除,只有咖啡精华储存在罐子里——只有对象的状态(字段值)被转换为字节流,而不是它的方法。

    【讨论】:

      【解决方案3】:

      正如 fvu 所述,序列化是将类的实例转换为字节数组的过程,反之亦然。并非 Java 中的每个类都可以转换为字节数组(想想 Thread 类,将线程转换为字节数组是没有意义的),这就是您(和我们)需要在那些可以被转换的对象中实现Serializable

      Serializable 只是一个标记接口,它不需要实现任何方法。在大多数情况下,这已经足够了:只需让您的可序列化对象实现接口,然后使用默认行为。

      默认的序列化行为在ObjectInputStreamObjectOutputStream 类中实现。 FileInputStreamFileOutputStream 是不同的类,用于从磁盘上的文件中读取和写入数据。

      如果你想将一个对象写入磁盘,你需要使用这样的东西:

      MyObject obj = ... // your object instance
      ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("/path/to/file"));
      stream.writeObject(obj);
      

      要回读对象,你需要这个:

      ObjectInputStream stream = new ObjectInputStream(new FileInputStream("/path/to/file"));
      MyObject obj = (MyObject) stream.readObject();
      

      但有时您希望将对象实例序列化/反序列化到内存中,而不是磁盘中,然后您会使用这样的东西:

      MyObject obj = ... // your object instance
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos);
      oos.writeObject(obj);
      byte[] data = baos.toByteArray();
      

      我将省略从字节数组中读回对象的部分,因为它非常简单,但考虑到您可能使用的巨大组合将是 JVM 提供的所有不同的输入/输出流。


      上面的示例使用默认的序列化行为。但是,您可能会遇到要序列化的给定类包含对未实现Serializable 接口的其他类实例的引用的情况,那么我们如何序列化它们呢?我们需要使用 transient 修饰符标记这些引用,以便默认的序列化行为将忽略它们,并且我们需要覆盖提供私有 readObjectwriteObject 方法的默认行为,这些方法将在尝试序列化/反序列化您的对象时被调用实例。

      此外,当序列化发生时,某些类可能会提供替换对象(不同可序列化类的对象实例)。该替换对象将包含原始类的状态,该原始类可以从该替换对象中恢复。此行为是通过在原始类中提供 writeReplace 方法和在替换类中提供 readReplace 来实现的。请注意,原始类和替换类都必须实现Serializable 接口,但只有替换对象中的数据会被序列化

      最后但并非最不重要的是,可以选择完全覆盖实现Externalizable 接口的默认序列化协议。这不是 Serializable 那样的标记接口,您必须实现将对象状态转换为数组的方法,反之亦然。您仍将使用 ObjectInputStream/ObjectOutputStream 对序列化 (externalise??) externalizable 对象实例,但现在将您的类转换为数组的逻辑字节数不再是 JVM 提供的字节数,而是您在实现 Externalizable 类时在类中编写的字节数。

      所有这些信息都在 thinksteep 作为评论提供的link 中。

      【讨论】:

        【解决方案4】:

        序列化是获取一个对象并将其写入字节数组(稍后从字节数组读取到对象)的过程

        ObjectOutputStream 内部使用序列化,它使用序列化将对象转换为 byte[] 并将其写入其输出

        【讨论】:

          猜你喜欢
          • 2013-07-21
          • 2017-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-01-17
          • 2015-02-02
          • 2020-12-21
          • 2018-11-26
          相关资源
          最近更新 更多