这些Java教程是针对JDK 8编写的。本页中描述的示例和实践不利用后续版本引入的改进,并可能使用不再可用的技术。
请查看Java语言更改以获取Java SE 9和后续版本中更新的语言特性的摘要。
请查看JDK发布说明以获取有关所有JDK版本的新功能、增强功能和已删除或不推荐选项的信息。
与数据流支持原始数据类型的I/O一样,对象流支持对象的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
遍历整个对象引用网络,并将该网络中的所有对象写入流。因此,单个writeObject
调用可能导致大量对象被写入流中。
这在下图中演示,其中调用writeObject
来写入一个名为a的单个对象。该对象包含对对象b和c的引用,而b包含对d和e的引用。调用writeobject(a)
不仅写入a,还写入重新构建a所需的所有对象,因此该网络中的其他四个对象也被写入。当通过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
,它们都是对同一个对象的引用。
然而,如果一个对象被写入两个不同的流中,它实际上是复制的 - 一个程序读取两个流将看到两个不同的对象。