# 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 推薦讀物