在java語言 I/O庫的設計中,使用了兩個結構模式,即裝飾模式和適配器模式。
在任何一種計算機語言中,輸入/輸出都是一個很重要的部分。與通常的計算機語言相比,java將輸入/輸出的功能和使用範疇作了很大的擴充。所以輸入輸出在java語言中佔有極爲重要的位置。java語言採用流的機制來實現輸入/輸出。所謂流,就是數據的有序排列,流能夠是從某個源(稱爲流源,或者 Source of Stream)出來,到某個目的(Sink of Stream)地去。根據流的方向能夠將流分紅輸出流和輸入流。程序經過輸入流讀取數據,想輸出流寫出數據。
例如:一個java程序可使用FileInputStream類從一個磁盤文件讀取數據,以下圖:java
像FileInputStream這樣的處理器叫流處理器。一個流處理器就像一個流的管道同樣,從一個流源吸入某種類型的數據,並輸出某種類型的數據。上面的示意圖叫流的管道圖。相似地,也能夠用FileOutputStream類向一個磁盤文件寫數據,以下圖:
在實際的應用當中,這樣簡單的機制並無太大的用處。程序須要寫出的每每是很是結構話的信息,所以這些Byte類型的數據其實是一些數字、文字、源代碼等。java的I/O庫提供了一個稱做連接(Chaining)的機制,能夠將一個流處理器與另外一個流處理器首尾相接,以其中之一的輸出爲輸入,造成一個流管道的連接。例如,DateInputStream流處理器能夠把FileInputStream流對象的輸出當作輸入,將Byte類型的數據轉換成java的原始數據類型和String數據類型,以下圖:
相似地,向一個文件中寫入Byte類型的數據也不是一個簡單的過程。一個程序須要向一個文件裏寫入的數據每每是結構化的,而Byte類型則是原始的類型,所以,在寫入的時候必須首先通過轉換。DateOutputStream流處理器提供了接受原始數據類型和String數據類型的方法,而這個流處理器的輸出數據則是Byte類型。換而言之,DateOutputStream能夠將源數據轉換成Byte類型的數據,在輸出出來。這樣一來,就能夠將DateOutputStream與FileOutputStream連接起來。這樣作的結果就是,程序能夠將原始數據類型和 String數據類型的源數據寫到這個連接好的雙重管道里面,達到將結構話數據寫到磁盤文件裏的目的,以下圖所示:
這是連接的威力。流處理器所處理的流一定都有流源,若是將流類所處理的流源分類的話,那麼基本能夠分紅兩大類:
(1)數組、String、File等,這一種叫原始流源。
(2)一樣類型的流用作連接流類的流源,就叫作連接流源。編程
java I/O庫的設計原則 設計模式
java語言的I/O庫是對各類常見的流源、流匯以及處理過程的抽象化。客戶端的java 程序沒必要知道最終的的流源、流匯是磁盤上的文件仍是一個數組,或者是一個線程;也不比插手到諸如數據是否緩存、能否按照行號讀取等處理的細節中去。要理解java I/O 這個龐大而複雜的庫,關鍵是掌握兩個對稱性和兩個設計模式。
java I/O庫的兩個對稱性 數組
java I/O庫具備兩個對稱性,它們分別是:
(1)輸入-輸出對稱:好比InputStream和OutputStream各自佔據Byte流的輸入和輸出的兩個平行的等級結構的根部;而Reader和Writer各自佔據Char流的輸入和輸出的兩個平行的等級結構的根部。
(2)byte-char對稱:InputStream和Reader的子類分別負責byte和插入流的輸入;OutputStream和Writer的子類分別負責byte和Char流的輸出,它們分別造成平行的等級結構。
java I/O庫的兩個設計模式 緩存
Java I/O庫的整體設計是符合裝飾模式和適配器模式的。如前所述,這個庫中處理流的類叫流類。
裝飾模式:在由InputStream、OutputStream、Reader和Writer表明的等級結構內部,有一些流處理器能夠對另外一些流處理器起到裝飾做用,造成新的、具備改善了的功能的流處理器。
適配器模式:在由InputStream、OutputStream、Reader和Writer表明的等級結構內部,有一些流處理器是對其餘類型的流處理器的適配。這就是適配器的應用。
裝飾模式的應用 多線程
裝飾模式在java中的最著名的應用莫過於java I/O標準庫的設計了。
因爲java I/O庫須要不少性能的各類組合,若是這些性能都是用繼承來實現,那麼每一種組合都須要一個類,這樣就會形成大量行重複的類出現。若是採用裝飾模式,那麼類的數目就會大大減小,性能的重複也能夠減至最少。所以裝飾模式是java I/O庫基本模式。裝飾模式的引進,形成靈活性和複雜性的提升。所以在使用 java I/O 庫時,必須理解java I/O庫是由一些基本的原始流處理器和圍繞它們的裝飾流處理器所組成的。
InputStream類型中的裝飾模式
InputStream有七個直接的具體子類,有四個屬於FilterInputStream的具體子類,以下圖所示:
上圖中全部的類都叫作流處理器,這個圖叫作(InputStream類型)流處理器圖。根據輸入流的源的類型,能夠將這些流分爲兩種,即原始流類和連接流處理器。
原始流處理器 ide
原始流處理器接收一個Byte數組對象、String對象、FileDescriptor對象或者不一樣類型的流源對象(就是前面所說的原始流源),並生成一個InputStream類型的流對象。在InputStream類型的流處理器中,原始流處理器包括如下四種:
(1)ByteArrayInputStream:爲多線程的通信提供緩衝區操做工做,接受一個Byte數組做爲流的源。
(2)FileInputStream:創建一個與文件有關的輸入流。接受一個File對象做爲流的源。
(3)PipedInputStream:能夠和PipedOutputStream配合使用,用於讀入一個數據管道的數據。接受一個PipedOutputStream做爲源。
(4)StringBufferInputStream:將一個字符串緩衝區抓換爲一個輸入流。接受一個String對象做爲流的源。
與原始流處理器相對應的是連接流處理器。
連接流處理器 性能
所謂連接流處理器就是能夠接受另外一個(同種類的)流對象(就是連接流源)做爲流源,並對之進行功能擴展的類。InputStream類型的連接流處理器包括如下幾種,它們接受另外一個InputStream對象做爲流源。
(1)FilterInputStream稱爲過濾輸入流,它將另外一個輸入流做爲流源。這個類的子類包括如下幾種:
BufferInputStream:用來從硬盤將數據讀入到一個內存緩衝區中,並今後緩衝區提供數據。
DateInputStream:提供基於多字節的讀取方法,能夠讀取原始數據類型的數據。
LineNumberInputStream:提供帶有行計算功能的過濾輸入流。
PushbackInputStream: 提供特殊的功能,能夠將已讀取的直接「推回」輸入流中。
(2)ObjectInputStream 能夠將使用ObjectInputStream串行化的原始數據類型和對象從新並行化。
(3)SequenceInputStream能夠將兩個已有的輸入流鏈接起來,造成一個輸入流,從而將多個輸入流排列構成一個輸入流序列。
必須注意的是,雖然PipedInuptStream接受一個流對象PipedOutputStream做爲流的源,可是PipedOutputStream流對象的類型不是InputStream,所以PipedInputStream流處理器仍屬於原始流處理器。
抽象結構圖
上面流處理器圖與裝飾模式的結構圖有明顯的相同之處。實際上InputStream類型的流處理器結構確實符合裝飾模式,而這能夠從它們在結構中所扮演的角色中分辯出來。
裝飾模式的各個角色
在全部InputStream類型的連接流處理其中,使用頻率最大的就是FilterInputStream類,以這個類爲抽象裝飾角色的裝飾模式結構很是明顯和典型。以這個類爲核心說明裝飾模式的各個角色是由哪些流處理器扮演:
抽象構件(Component)角色:由InputStream扮演。這是一個抽象類,爲各類子類型處理器提供統一的接口。
具體構建(Concrete Component)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream以及StringBufferInputStream等原始流處理器扮演。它們實現了抽象構建角色所規定的接口,能夠被連接流處理器所裝飾。
抽象裝飾(Decorator)角色:由FilterInputStream扮演。它實現了InputStream所規定的接口。
具體裝飾(Concrete Decorator)角色:由幾個類扮演,分別是DateInputStream、BufferedInputStream 以及兩個不經常使用到的類LineNumberInputStream和PushbackInputStream。
連接流其實就是裝飾角色,原始流就是具體構建角色,以下圖所示:
一方面,連接流對象接受一個(同類型的)原始流對象或者另外一個(同類型的)連接流對象做爲流源;另外一方面,它們都對流源對象的內部工做方法作了相應的改變,這種改變是裝飾模式所要達到的目的。好比:
(1)BufferedInputStream 「裝飾」 了InputStream的內部工做方式,使得流的讀入操做使用緩衝機制。在使用了緩衝機制後,不會對每一次的流讀入操做都產生一個物理的讀盤動做,從而提升了程序的效率。在涉及到物理流的讀入時,都應當使用這個裝飾流類。
(2)LineNumberInputStream和PushbackInputStream也一樣「裝飾」了InputStream的內部工做方式, 前者使得程序可以按照行號讀入數據;後者能使程序在讀入的過程當中退後一個字符。後兩個裝飾類可能在實際的編程工做中不多用到,由於它們是爲了支持用 java語言作編譯器而準備的。
(3)DateInputStream子類讀入各類不一樣的原始數據類型以及String類型的數據,這一點能夠從它提供的各類read()方法看出來:readByte()、readUnsignedByte()、readShort()、readUnsignedShort()、readChar()、readInt()、readLong()、readFloat()、readDouble()、readUTF()。使用這個流處理器以及它的搭檔DateOutputStream,能夠將原始數據經過流從一個地方移到另外一個地方。
OutputStream 類型中的裝飾模式 編碼
outputStream是一個用於輸出的抽象類,它的接口、子類的等級結構、子類的功能都和InputStream有很好的對稱性。在OutputStream給出的接口裏,將write換成read就獲得了InputStream的接口,而其具體子類則在功能上面是平行的。 spa
(1)針對byte數字流源的連接流類,以ByteArrayInputStream描述輸入流,以ByteArrayOutputStream描述輸出流。
(2)針對String流源的連接流類,以StringBufferInputStream描述輸入流,以StringBufferOutputStream描述輸出流。
(3)針對文件流源的連接流類,以FileInputStream描述輸入流,以FileOutputStream描述輸出流。
(4)針對數據管道流源的連接流類,以PipedInputStream描述輸入流,以PipedOutputStream描述輸出流。
(5)針對以多個流組成的序列,以SequenceInputStream描述輸入流,以SequenceOutputStream描述輸出流。
OutputStream類型有哪些子類
outputStream有5個直接的具體子類,加上三個屬於FilterInputStream的具體子類,一共有8個具體子類,以下圖:
原始流處理器
在OutputStream類型的流處理器中,原始流處理器包括如下三種:
連接流處理器
OutputStream類型的連接流處理器包括如下幾種:
裝飾模式的各個角色
在全部的連接流處理器中,最多見的就是FilterOutputStream類。以這個類爲核心的裝飾模式結構很是明顯和典型,以下圖:
裝飾模式所涉及的各個角色:
抽象構件(Component)角色:由OutputStream扮演,這是一個抽象類,爲各類的子類型流處理器提供統一的接口。
具體構件(Concrete Component)角色:由ByteArrayOutputStream、FileOutputStream、PipedOutputStream等扮演,它們均實現了OutputStream所聲明的接口。
抽象裝飾(Decorator)角色:由FilterOutputStream扮演,它與OutputStream有相同的接口,而這正是裝飾類的關鍵。
具體裝飾(Concrete Decorator)角色:由幾個類扮演,分別是BufferedOutputStream、DateOutputStream、以及PrintStream。
所謂連接流,就是裝飾模式中的裝飾角色,原始流就是具體構件角色。
與 DateInputStream相對應的是DataOutputStream,後者負責將由原始數據類型和String對象組成的數據格式化,並輸出到一個流中,使得任何機器上的任何DataInputStream類型的對象均可以讀入這些數據。全部的方法都是以write開始。
若是須要對數據進行真正的格式化,以便輸出到像控制檯顯示那樣,那就須要使用PrintStream。PrintStream能夠對由原始數據類型和String對象組成的數據進行格式化,以造成能夠閱讀的格式;而DataOutputStream則不一樣,它將數據輸出到一個流中,以便DataInputStream能夠在任何機器然後操做系統中均可以從新將數據讀入,並進行結構重建。PrintStream對象最重要的兩個方法是print() 和println(),這兩個方法都是重載的,以即可以打印出全部使用類型的數據。這兩個方法之間的區別是後者每行結束時多打印出一個換行符號。
BufferedOutputStream對一個輸出流進行裝飾,使得流的寫出操做使用緩衝機制。在使用緩衝機制後,不會對每一次的流的寫入操做都產生一 個物理的寫動做,從而提升的程序的效率。在涉及到物理流的地方,好比控制檯I/O、文件I/O等,都應當使用這個裝飾流處理器。
Reader類型中的裝飾模式
在Reader類型的流處理器中,原始流處理器包括如下四種:
(1)CharArrayReader:爲多線程的通訊提供緩衝區操做功能。
(2)InputStreamReader:這個類有一個子類--FileReader。
(3)PipedReader:能夠與PipedOutputStream配合使用,用於讀入一個數據管道的數據。
(4)StringReader:創建一個與文件有關的輸入流。
連接流處理器包括如下:
(1)BufferedReader:用來從硬盤將數據讀入到一個內存緩衝區,並今後緩衝區提供數據,這個類的子類爲LineNumberReader。
(2)FilterReader:成爲過濾輸入流,它將另外一個輸入流做爲流的來源。這個類的子類有PushbackReader,提供基於多字節的讀取方法,能夠讀取原始數據類型的數據,Reader類型的類圖以下所示:
Reader類型中,裝飾模式所涉及的各個角色:
(1)抽象構建(Component)角色:有Reader扮演。這是一個抽象類,爲各類的子類型流處理器提供統一的接口。
(2)具體構建(Concrete Component)角色:由CharArrayReader、InputStreamReader、PiPedReader、StringReader等扮演,他們均實現了Reader所聲明的接口。
(3)抽象裝飾(Decorator)角色:由BufferedReader和FilterReader扮演。這二者有着與Reader相同的接口,它們 分別給出兩個裝飾角色的等級結構,第一個給出LineNumberReader做爲具體裝飾角色,另外一個給出PushbackReader 做爲具體裝飾角色。
(4)具體裝飾(Concrete Decorator)角色:LineNumberReader做爲BufferedReader的具體裝飾角色,BufferedReader做爲FilterReader的具體裝飾角色。
以下圖所示,標有聚合連線的就是抽象裝飾角色:
Writer類型中的裝飾模式
Writer類型是一個與Reader類型平行的等級結構,並且Writer類型的等級結構幾乎與Reader的等級結構關於輸入/輸出是對稱的。如圖所示:
在Writer類型的流處理器中,原始流處理器包括如下四種:
(1)CharArrayWriter:爲多線程的通訊提供緩衝區的操做功能。
(2)OutputStreamWriter:創建一個與文件有關的輸出流。含有一個具體子類FileWrite,爲Write類型的輸出流提供文件輸出功能。
(3)PipedWriter:能夠和PipedOutputStream配合使用,用於讀若是一個數據管道的數據。
(4)StringWriter:想一個StringBuffer寫出數據。
連接流處理器包括如下三種:
(1)BufferedWriter:爲Writer類型的流處理器提供緩衝區功能。
(2)FilterWriter:稱爲過濾輸入流,它將另外一個輸入流做爲流的來源。這是一個沒有子類的抽象類。
(3)PrintWriter:支持格式化的文字輸出。
Writer類型中,裝飾模式所涉及的各個角色:
(1)抽象構建(Component)角色:由Write扮演。這是一個抽象類,爲爲各類子類型的流處理器提供統一的接口。
(2)具體構建(Concrete Component):角色由CharArrayWriter、OutputStreamWriter、
PipedWriter、StringWriter扮演,它們實現了Writer所聲明的接口。
(3)抽象裝飾(Decorator)角色:由BufferedWriter、FilterWriter、PrintWriter扮演,它們有着與Write
相同的接口。
(4)具體裝飾(Concrete Decorator)角色:與抽象裝飾角色合併。
以下圖所示,標出了從抽象裝飾角色到抽象構件角色的聚合連線,更易於與裝飾模式的結構圖比較。
適配器模式的應用
適配器模式是java I/O庫中第二個最重要的設計模式。
InputStream原始流處理器中的適配器模式
InputStream類型的原始流處理器是適配器模式的應用。
ByteArrayInputStream是一個適配器類 。ByteArrayInputStream繼承了InputStream的接口,而封裝了一個byte數組。換而言之,它將一個byte數組的接口適配成了InputStream流處理器的接口。 java語言支持四種類型:java類、java接口、java數組和原始類型。前三章是引用類型,類和數組的實例都是對象,原始類型的值很多對象。 java語言的數組是像全部其餘對象同樣的對象,而無論數組中所存放的元素的類型是什麼。這樣一來,ByteArrayInputStream就符合適配 器模式的描述,並且是一個對象形式的適配器類。以下圖所示:
StringBufferInputStream是一個適配器類。StringBufferInputStream繼承了InputStream類型,同時持有一個對String類型的引用。這是將String對象適配成InputStream類型的對象形式的適配器模式,以下圖:
OutputStream原始流處理器中的適配器模式
在OutputStream類型中,全部的原始流處理器都是適配器類。ByteArrayOutputStream是一個適配器類。ByteArrayOutputStream繼承了OutputStream類型,同事持有一個對byte數組的引用。它把一個byte數組的接口適配成OutputStream類型的接口,所以也是一個對象類型的適配器模式的應用。以下圖:
FileOutputStream是一個適配器類FileOutputStream繼承OutputStream,同時持有一個對FileDescriptor對象的引用。這是一個將FileDescriptor適配成OutputStream接口的對象形式的適配器模式,以下圖所示:
PipedOutputStream是一個適配器類。PipedOutputStream老是和PipedInputStream一塊兒使用,它接收一個類型爲PipedInputStream的輸入類型,並將之轉換成OutputStream類型的輸出流,這是一個對象形式的適配器模式應用。以下圖:
Reader原始流處理器中的適配器模式
Reader 類型的原始流處理器都是適配器模式的應用。CharArrayReader是一個適配器類。CharArrayReader將一個Char數組適配成Reader類型的輸入流,所以它是一個對象形式的適配器應用,以下圖所示:
StringReader是一個適配器類。StringReader 繼承了Reader類型,持有一個對String類型的引用。它將String的接口適配成Reader類型的接口,以下圖所示:
Writer類型中的適配器模式
Writer類型中的原始流處理器就是適配器模式的具體應用。CharArrayWriter是一個適配器類。CharArrayWriter將一個Char數組適配成Writer 接口,以下圖所示:
PipedWriter是一個適配器類。PipedWriter老是與PiPedReader一同使用,它將一個PipedReader對象的接口適配成一個Writer類型的接口,以下圖所示:
StringWriter是一個適配器類。StringWriter繼承Writer類型,同時持有一個StringBuffer對象,它將StringBuffer對象的接口適配成爲了。Writer類型的接口,是一個對象形式的適配器 模式的應用,以下圖所示:
從byte流到char流的適配
在java語言的標準庫 java I/O 裏面,有一個InputStreamReader類叫作橋樑(bridge)類。InputStreamReader是從byte流到char流的一個橋 梁,它讀入byte數據並根據指定的編碼將之翻譯成char數據。InputStreamReader雖然叫「橋樑」,但它不是橋樑模式,是適配器模式的應用。InputStreamReader是從byte輸入流到char輸入流的一個適配器。下圖所示就是InputStreamReader 的結構圖:
爲了說明適配器類InputStreamReader是如何使用,請看下面例子。Echo類能夠將控制檯輸入的任何字符串重新打印出來,源代碼以下:
package com.think.cla; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class Echo { public static void main(String [] args)throws IOException{ String line; InputStreamReader input = new InputStreamReader(System.in); System.out.println("Enter data and push enter:"); BufferedReader reader = new BufferedReader(input); line = reader.readLine(); System.out.println("Data entered :"+line); } }
能夠看出,這個類接受一個類型爲inputStream的System.in對象,將之適配成Reader類型,而後再使用 BufferedReader類"裝飾"它,將緩衝功能加上去。這樣一來,就可使BufferedReader對象的readerLine() 方法讀入整行的輸入數據,數據類型是String。 在獲得這個數據以後,程序又將它寫出到System.out 中去,完成了所有的流操做,下圖所示爲其管道圖:
本系統使用了BufferedReader來爲流的讀入提供緩衝功能,這樣作的直接效果是可使用readLine()方法按行讀入數據。可是因爲 Reader接口並不提供readLine()方法,因此這樣一來,系統就必須聲明一個BufferedReader類型的流處理器,而不是一個 Reader類型的流處理器,這意味着裝飾模式的退化。
在上面的管道鏈接過程當中,InputStreamReader 起到了適配器的做用,它將一個byte類型的輸入流適配成爲一個char類型的輸入流。在這以後,BufferedReader則起到了裝飾模式的做用, 將緩衝機制引入到流的讀入中。所以這個例子涉及到了兩個設計模式。