# 10.2 增添属性和有用的接口
利用层次化对象动态和透明地添加单个对象的能力的做法叫作“装饰器”(Decorator)方案——“方案”属于本书第16章的主题(注释①)。装饰器方案规定封装于初始化对象中的所有对象都拥有相同的接口,以便利用装饰器的“透明”性质——我们将相同的消息发给一个对象,无论它是否已被“装饰”。这正是在Java IO库里存在“过滤器”(Filter)类的原因:抽象的“过滤器”类是所有装饰器的基类(装饰器必须拥有与它装饰的那个对象相同的接口,但装饰器亦可对接口作出扩展,这种情况见诸于几个特殊的“过滤器”类中)。
子类处理要求大量子类对每种可能的组合提供支持时,便经常会用到装饰器——由于组合形式太多,造成子类处理变得不切实际。Java IO库要求许多不同的特性组合方案,这正是装饰器方案显得特别有用的原因。但是,装饰器方案也有自己的一个缺点。在我们写一个程序的时候,装饰器为我们提供了大得多的灵活性(因为可以方便地混合与匹配属性),但它们也使自己的代码变得更加复杂。原因在于Java IO库操作不便,我们必须创建许多类——“核心”IO类型加上所有装饰器——才能得到自己希望的单个IO对象。
`FilterInputStream`和`FilterOutputStream`(这两个名字不十分直观)提供了相应的装饰器接口,用于控制一个特定的输入流(`InputStream`)或者输出流(`OutputStream`)。它们分别是从`InputStream`和`OutputStream`派生出来的。此外,它们都属于抽象类,在理论上为我们与一个流的不同通信手段都提供了一个通用的接口。事实上,`FilterInputStream`和`FilterOutputStream`只是简单地模仿了自己的基类,它们是一个装饰器的基本要求。
## 10.2.1 通过`FilterInputStream`从`InputStream`里读入数据
`FilterInputStream`类要完成两件全然不同的事情。其中,`DataInputStream`允许我们读取不同的基本类型数据以及`String`对象(所有方法都以`read`开头,比如`readByte()`,`readFloat()`等等)。伴随对应的`DataOutputStream`,我们可通过数据“流”将基本类型的数据从一个地方搬到另一个地方。这些“地方”是由表10.1总结的那些类决定的。若读取块内的数据,并自己进行解析,就不需要用到`DataInputStream`。但在其他许多情况下,我们一般都想用它对自己读入的数据进行自动格式化。
剩下的类用于修改`InputStream`的内部行为方式:是否进行缓冲,是否跟踪自己读入的数据行,以及是否能够推回一个字符等等。后两种类看起来特别象提供对构建一个编译器的支持(换言之,添加它们为了支持Java编译器的构建),所以在常规编程中一般都用不着它们。
也许几乎每次都要缓冲自己的输入,无论连接的是哪个IO设备。所以IO库最明智的做法就是将未缓冲输入作为一种特殊情况处理,同时将缓冲输入接纳为标准做法。
表10.3 `FilterInputStream`的类型
```
Class
Function
Constructor Arguments
How to use it
Data-InputStream
Used in concert with DataOutputStream, so you can read primitives (int, char, long, etc.) from a stream in a portable fashion.
InputStream
Contains a full interface to allow you to read primitive types.
Buffered-InputStream
Use this to prevent a physical read every time you want more data. You’re saying “Use a buffer.”
InputStream, with optional buffer size.
This doesn’t provide an interface per se, just a requirement that a buffer be used. Attach an interface object.
LineNumber-InputStream
Keeps track of line numbers in the input stream; you can call getLineNumber( ) and setLineNumber(int).
InputStream
This just adds line numbering, so you’ll probably attach an interface object.
Pushback-InputStream
Has a one byte push-back buffer so that you can push back the last character read.
InputStream
Generally used in the scanner for a compiler and probably included because the Java compiler needed it. You probably won’t use this.
```
| 类 | 功能 | 构造器参数/如何使用
| --- | --- | --- |
| `DataInputStream` | 与`DataOutputStream`联合使用,使自己能以机动方式读取一个流中的基本数据类型(`int`,`char`,`long`等等) | `InputStream`/包含了一个完整的接口,以便读取基本数据类型 |
| `BufferedInputStream` | 避免每次想要更多数据时都进行物理性的读取,告诉它“请先在缓冲区里找” | `InputStream`,没有可选的缓冲区大小/本身并不能提供一个接口,只是发出使用缓冲区的要求。要求同一个接口对象连接到一起 |
| `LineNumberInputStream` | 跟踪输入流中的行号;可调用`getLineNumber()`以及`setLineNumber(int) `| 只是添加对数据行编号的能力,所以可能需要同一个真正的接口对象连接 |
| `PushbackInputStream` | 有一个字节的后推缓冲区,以便后推读入的上一个字符 | `InputStream`/通常由编译器在扫描器中使用,因为Java编译器需要它。一般不在自己的代码中使用 |
## 10.2.2 通过`FilterOutputStream向OutputStream`里写入数据
与`DataInputStream`对应的是`DataOutputStream`,后者对各个基本数据类型以及`String`对象进行格式化,并将其置入一个数据“流”中,以便任何机器上的`DataInputStream`都能正常地读取它们。所有方法都以`wirte`开头,例如`writeByte()`,`writeFloat()`等等。
若想进行一些真正的格式化输出,比如输出到控制台,请使用`PrintStrea`m。利用它可以打印出所有基本数据类型以及`String`对象,并可采用一种易于查看的格式。这与`DataOutputStream`正好相反,后者的目标是将那些数据置入一个数据流中,以便`DataInputStream`能够方便地重新构造它们。`System.out`静态对象是一个`PrintStream`。
`PrintStream`内两个重要的方法是`print()`和`println()`。它们已进行了覆盖处理,可打印出所有数据类型。`print()`和`println()`之间的差异是后者在操作完毕后会自动添加一个新行。
`BufferedOutputStream`属于一种“修改器”,用于指示数据流使用缓冲技术,使自己不必每次都向流内物理性地写入数据。通常都应将它应用于文件处理和控制器IO。
表10.4 `FilterOutputStream`的类型
```
Class
Function
Constructor Arguments
How to use it
Data-OutputStream
Used in concert with DataInputStream so you can write primitives (int, char, long, etc.) to a stream in a portable fashion.
OutputStream
Contains full interface to allow you to write primitive types.
PrintStream
For producing formatted output. While DataOutputStream handles the storage of data, PrintStream handles display.
OutputStream, with optional boolean indicating that the buffer is flushed with every newline.
Should be the “final” wrapping for your OutputStream object. You’ll probably use this a lot.
Buffered-OutputStream
Use this to prevent a physical write every time you send a piece of data. You’re saying “Use a buffer.” You can call flush( ) to flush the buffer.
OutputStream, with optional buffer size.
This doesn’t provide an interface per se, just a requirement that a buffer is used. Attach an interface object.
```
| 类 | 功能 | 构造器参数/如何使用 |
| --- | --- | --- |
| `DataOutputStream` | 与`DataInputStream`配合使用,以便采用方便的形式将基本数据类型(`int`,`char`,`long`等)写入一个数据流 | `OutputStream`/包含了完整接口,以便我们写入基本数据类型 |
| `PrintStream` | 用于产生格式化输出。 | `DataOutputStream`控制的是数据的“存储”,而`PrintStream`控制的是“显示” |
| `OutputStream` | | 可选一个布尔参数,指示缓冲区是否与每个新行一同刷新/对于自己的OutputStream对象,应该用`final`将其封闭在内。可能经常都要用到它 |
| `BufferedOutputStream` | 用它避免每次发出数据的时候都要进行物理性的写入,要求它“请先在缓冲区里找”。可调用`flush()`,对缓冲区进行刷新 | `OutputStream`,可选缓冲区大小/本身并不能提供一个接口,只是发出使用缓冲区的要求。需要同一个接口对象连接到一起 |
- Java 编程思想
- 写在前面的话
- 引言
- 第1章 对象入门
- 1.1 抽象的进步
- 1.2 对象的接口
- 1.3 实现方案的隐藏
- 1.4 方案的重复使用
- 1.5 继承:重新使用接口
- 1.6 多态对象的互换使用
- 1.7 对象的创建和存在时间
- 1.8 异常控制:解决错误
- 1.9 多线程
- 1.10 永久性
- 1.11 Java和因特网
- 1.12 分析和设计
- 1.13 Java还是C++
- 第2章 一切都是对象
- 2.1 用引用操纵对象
- 2.2 所有对象都必须创建
- 2.3 绝对不要清除对象
- 2.4 新建数据类型:类
- 2.5 方法、参数和返回值
- 2.6 构建Java程序
- 2.7 我们的第一个Java程序
- 2.8 注释和嵌入文档
- 2.9 编码样式
- 2.10 总结
- 2.11 练习
- 第3章 控制程序流程
- 3.1 使用Java运算符
- 3.2 执行控制
- 3.3 总结
- 3.4 练习
- 第4章 初始化和清除
- 4.1 用构造器自动初始化
- 4.2 方法重载
- 4.3 清除:收尾和垃圾收集
- 4.4 成员初始化
- 4.5 数组初始化
- 4.6 总结
- 4.7 练习
- 第5章 隐藏实现过程
- 5.1 包:库单元
- 5.2 Java访问指示符
- 5.3 接口与实现
- 5.4 类访问
- 5.5 总结
- 5.6 练习
- 第6章 类复用
- 6.1 组合的语法
- 6.2 继承的语法
- 6.3 组合与继承的结合
- 6.4 到底选择组合还是继承
- 6.5 protected
- 6.6 累积开发
- 6.7 向上转换
- 6.8 final关键字
- 6.9 初始化和类装载
- 6.10 总结
- 6.11 练习
- 第7章 多态性
- 7.1 向上转换
- 7.2 深入理解
- 7.3 覆盖与重载
- 7.4 抽象类和方法
- 7.5 接口
- 7.6 内部类
- 7.7 构造器和多态性
- 7.8 通过继承进行设计
- 7.9 总结
- 7.10 练习
- 第8章 对象的容纳
- 8.1 数组
- 8.2 集合
- 8.3 枚举器(迭代器)
- 8.4 集合的类型
- 8.5 排序
- 8.6 通用集合库
- 8.7 新集合
- 8.8 总结
- 8.9 练习
- 第9章 异常差错控制
- 9.1 基本异常
- 9.2 异常的捕获
- 9.3 标准Java异常
- 9.4 创建自己的异常
- 9.5 异常的限制
- 9.6 用finally清除
- 9.7 构造器
- 9.8 异常匹配
- 9.9 总结
- 9.10 练习
- 第10章 Java IO系统
- 10.1 输入和输出
- 10.2 增添属性和有用的接口
- 10.3 本身的缺陷:RandomAccessFile
- 10.4 File类
- 10.5 IO流的典型应用
- 10.6 StreamTokenizer
- 10.7 Java 1.1的IO流
- 10.8 压缩
- 10.9 对象序列化
- 10.10 总结
- 10.11 练习
- 第11章 运行期类型识别
- 11.1 对RTTI的需要
- 11.2 RTTI语法
- 11.3 反射:运行期类信息
- 11.4 总结
- 11.5 练习
- 第12章 传递和返回对象
- 12.1 传递引用
- 12.2 制作本地副本
- 12.3 克隆的控制
- 12.4 只读类
- 12.5 总结
- 12.6 练习
- 第13章 创建窗口和程序片
- 13.1 为何要用AWT?
- 13.2 基本程序片
- 13.3 制作按钮
- 13.4 捕获事件
- 13.5 文本字段
- 13.6 文本区域
- 13.7 标签
- 13.8 复选框
- 13.9 单选钮
- 13.10 下拉列表
- 13.11 列表框
- 13.12 布局的控制
- 13.13 action的替代品
- 13.14 程序片的局限
- 13.15 视窗化应用
- 13.16 新型AWT
- 13.17 Java 1.1用户接口API
- 13.18 可视编程和Beans
- 13.19 Swing入门
- 13.20 总结
- 13.21 练习
- 第14章 多线程
- 14.1 反应灵敏的用户界面
- 14.2 共享有限的资源
- 14.3 堵塞
- 14.4 优先级
- 14.5 回顾runnable
- 14.6 总结
- 14.7 练习
- 第15章 网络编程
- 15.1 机器的标识
- 15.2 套接字
- 15.3 服务多个客户
- 15.4 数据报
- 15.5 一个Web应用
- 15.6 Java与CGI的沟通
- 15.7 用JDBC连接数据库
- 15.8 远程方法
- 15.9 总结
- 15.10 练习
- 第16章 设计模式
- 16.1 模式的概念
- 16.2 观察器模式
- 16.3 模拟垃圾回收站
- 16.4 改进设计
- 16.5 抽象的应用
- 16.6 多重分发
- 16.7 访问器模式
- 16.8 RTTI真的有害吗
- 16.9 总结
- 16.10 练习
- 第17章 项目
- 17.1 文字处理
- 17.2 方法查找工具
- 17.3 复杂性理论
- 17.4 总结
- 17.5 练习
- 附录A 使用非JAVA代码
- 附录B 对比C++和Java
- 附录C Java编程规则
- 附录D 性能
- 附录E 关于垃圾收集的一些话
- 附录F 推荐读物