我对此感到困惑。因为在实现 Serializable 类时,我们需要使用类似的类FileOutputStream
,ObjectOutputStream
或者类似的东西。那么我们为什么不直接使用这些类来做一些事情,比如将对象输出到文件,从文件中输入对象来直接维护对象的状态呢?为什么我们要先实现 Serializable,然后再做同样的事情?
4 回答
这么理解...
Serializable 是标记接口,表示您的类的对象可以转换为字节流,并在需要时最终返回为 java 对象。最初您可能认为每个类都应该是可序列化的,但考虑到这是不正确的
Input- 和 OutputStreams 有一些文件句柄可供读取。当流变得不可用时,该文件句柄将关闭。所以这种情况下的序列化没有意义;并且反序列化永远不会恢复该句柄。
所以这应该回答为什么需要标记为可序列化?
现在实现定义对象应该如何写入或读取的方法;这应该由您定义,因此您需要所有这些流对象和 readObject、writeObject 方法。希望这能让你对这个概念有更多的理解。
Serializable只是一个标记接口,这意味着它仅用于向实际执行序列化的代码发出信号,您(程序员)知道(或希望:-))此类可以毫无问题地序列化。
序列化本身就是将对象转换为可以存储或传输的东西——即作为字节流。您可以将序列化与冷冻干燥进行比较,冷冻干燥是用于制造雀巢咖啡的技术——所有的水都被去除,只有咖啡精华被存储在一个罐子里——只有一个对象的状态(字段值)被转换为字节流,而不是它的方法。
正如 fvu 所述,序列化是将类的实例转换为字节数组的过程,反之亦然。并非 Java 中的每个类都可以转换为字节数组(想想Thread
类,将线程转换为字节数组是没有意义的),这就是您(和我们)需要实现Serializable
in那些可以转换的对象。
Serializable
只是一个标记接口,它不需要实现任何方法。在大多数情况下,这已经足够好了:只需让您的可序列化对象实现接口,然后使用默认行为。
该默认序列化行为在ObjectInputStream
和ObjectOutputStream
类中实现。FileInputStream
并且FileOutputStream
是不同的类,用于从磁盘上的文件中读取和写入数据。
如果要将对象写入磁盘,则需要使用以下内容:
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
修饰符标记这些引用,以便默认的序列化行为将忽略它们,我们需要覆盖默认行为,提供私有readObject
和writeObject
在尝试序列化/反序列化对象实例时将调用的方法。
此外,当序列化发生时,某些类可能会提供替换对象(不同可序列化类的对象实例)。该替换对象将包含原始类的状态,该原始类可以从该替换对象中恢复。此行为是通过提供writeReplace
原始类中的方法和readReplace
替换类中的方法来实现的。请注意,原始类和替换类都必须实现Serializable
接口,但只有替换对象中的数据会被序列化。
最后但同样重要的是,可以选择完全覆盖实现Externalizable
接口的默认序列化协议。这不是一个标记接口Serializable
,您必须实现将对象状态转换为数组的方法,反之亦然。您仍将使用ObjectInputStream
/ObjectOutputStream
对来序列化(外部化??)可外部化的对象实例,但现在将您的类转换为字节数组的逻辑不再是 JVM 提供的逻辑,而是您在类中编写的逻辑实现Externalizable
类。
所有这些信息都在thinksteep 作为评论提供的链接中。
序列化是获取一个对象并将其写入字节数组的过程(稍后从字节数组读取到对象)
ObjectOutputStream 内部使用序列化,它使用序列化将对象转换为 byte[] 并将其写入其输出