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