Java 教程是为 JDK 8 编写的。本页中描述的示例和实践未利用在后续版本中引入的改进。
正如数据流支持基本数据类型的 I/O 一样,对象流也支持对象的 I/O。大多数(但不是全部)标准类支持其对象的序列化。那些确实实现了标记接口 Serializable
。
对象流类是 ObjectInputStream
和 ObjectOutputStream
。这些类实现 ObjectInput
和 ObjectOutput
,它们是 DataInput
和 DataOutput
的子接口。这意味着 Data Streams 中涵盖的所有基本数据 I/O 方法也在对象流中实现。因此,对象流可以包含基本值和对象值的混合。ObjectStreams
示例说明了这一点。ObjectStreams
创建与 DataStreams
相同的应用程序,并进行了一些更改。首先,价格现在是 BigDecimal
对象,以更好地表示小数值。其次,将 Calendar
对象写入数据文件,指示发票日期。
如果 readObject()
未返回预期的对象类型,则尝试将其强制转换为正确的类型可能会抛出 ClassNotFoundException
。在这个简单的例子中,这不可能发生,因此我们不会尝试捕获异常。相反,我们通过将 ClassNotFoundException
添加到 main
方法的 throws
子句来通知编译器我们已经知道了这个问题。
writeObject
和 readObject
方法易于使用,但它们包含一些非常复杂的对象管理逻辑。这对像 Calendar 这样的类来说并不重要,它只封装了基本值。但是许多对象包含对其他对象的引用。如果 readObject
是要从流重构对象,则它必须能够重新构建原始对象所引用的所有对象。这些附加对象可能有自己的引用,依此类推。在这种情况下,writeObject
遍历整个对象引用网络,并将该网络中的所有对象写入流中。因此,单次调用 writeObject
会导致将大量对象写入流中。
下图演示了这一点,其中调用 writeObject
来写入名为 a 的单个对象。该对象包含对象 b 和 c 的引用,而 b 包含对 d 和 e 的引用。调用 writeobject(a)
不仅写入 a,而且还要写入重构 a 所需的所有对象,因此也写入此 Web 中的其他四个对象。当 readObject
读回 a 时,也会回读其他四个对象,并保留所有原始对象引用。
多个引用对象的 I/O.
你可能想知道如果同一个流上的两个对象都包含对单个对象的引用会发生什么。当他们回读时,他们都会引用一个对象吗?答案是肯定的。流可以只包含一个对象的副本,尽管它可以包含任意数量的对象。因此,如果你将对象显式写入流两次,那么你实际上只写入了两次引用。例如,如果以下代码将对象 ob
两次写入流:
Object ob = new Object(); out.writeObject(ob); out.writeObject(ob);
每个 writeObject
必须与 readObject
匹配,因此读回流的代码将如下所示:
Object ob1 = in.readObject(); Object ob2 = in.readObject();
这导致两个变量 ob1
和 ob2
,它们是对单个对象的引用。
但是,如果将单个对象写入两个不同的流,则它是有效地重复 读取两个流的单个程序将看到两个不同的对象。