天天看點

java io 擴充卡_簡述JAVA IO流以及IO流中的擴充卡模式、裝飾模式

摘要:讀完本章節,您對java 的IO流有更清晰深刻的認識,對擴充卡模式、裝飾模式也有初步的了解。

一、關于流引用百度百科上的解釋:

流是一種抽象概念,它代表了資料的無結構化傳遞。按照流的方式進行輸入輸出,資料被當成無結構的位元組序或字元序列。從流中取得資料的操作稱為提取操作,亦稱讀操作;而向流中添加資料的操作稱為插入操作,亦稱寫操作。用來進行輸入輸出操作的流就稱為IO流。換句話說說,IO流就是以流的方式進行輸入輸出。應用場景如Java Web上的檔案上傳與下載下傳、磁盤檔案的讀操作寫操作,這些都需要用到流。

二、流的分類:

按流的流向分為輸入流、輸出流,InputStream、Reader及其所有子類都是輸入流,OutputStream、Writer及其所有子類都是輸出流,從流中讀資料用輸入流,寫資料到流中用輸出流。

按流的資料機關分為位元組流、字元流,InputStream、OutputStream及其所有子類都是位元組流,Reader、Writer及其所有子類都是字元流,當操作流中的資料含有中文的時候,請務必使用字元流進行讀寫。如果流中含有中文而使用位元組流進行讀寫有可能出現中文亂碼、資料失真現象,因為資料如果是GBK編碼一個中文漢字含有兩個位元組,如果是UTF-8編碼一個中文漢字含有三個位元組,讀寫的時候如若處理不當,很容易産生中文亂碼。

按流的功能分為節點流、處理流(過濾流),節點流是指從特定地方讀寫的流類如磁盤或者一塊記憶體區域記憶體,比如ByteArrayInputStream、FileInputStream等,處理流是指通過已經存在的輸入輸出流構造的流,比如BufferedInputStream、DataInputStream等

三、輸入流詳解

a、位元組輸入流InputStream及其子類,

InputStream主要方法:

1、public abstract int read() throws IOException;

從輸入流中讀取資料的下一個位元組。傳回0到255範圍内的int值。如果因為已經到達流末尾而沒有可用的位元組, 則傳回-1。在輸入資料可用、檢測到流末尾或者抛出異常前,此方法一直阻塞。子類必須提供此方法的實作。

2、public int read(byte b[]) throws IOException {...};

從輸入流中讀取一定數量的位元組,并将其存儲在緩沖區數組b中。以整數形式傳回實際讀取的位元組個數。在輸入資料可用、檢測到檔案末尾或者抛出異常前,此方法一直阻塞。如果數組b的長度為0,則不讀取任何位元組并傳回0;否則,嘗試讀取至少一個位元組。如果因為流位于檔案末尾而沒有可用的位元組,則傳回值-1;否則,至少讀取一個位元組并将其存儲在b中。将讀取的第一個位元組存儲在元素b[0]中,下一個存儲在b[1]中,以此類推。讀取的位元組數最多等于b的長度。設k為實際讀取的位元組數;這些位元組将存儲在b[0]到b[k-1]中,不影響b[k]到b[b.length-1]的元素。類InputStream的read(b)方法的效果等同于read(b,0,b.length)。

3、public int read(byte b[], int off, int len) throws IOException {...};

将輸入流中最多 len 個資料位元組讀入 byte 數組。嘗試讀取 len 個位元組,但讀取的位元組也可能小于該值。以整數形式傳回實際讀取的位元組數。在輸入資料可用、檢測到流末尾或者抛出異常前,此方法一直阻塞。如果 len 為 0,則不讀取任何位元組并傳回 0;否則,嘗試讀取至少一個位元組。如果因為流位于檔案末尾而沒有可用的位元組,則傳回值 -1;否則,至少讀取一個位元組并将其存儲在 b 中。将讀取的第一個位元組存儲在元素 b[off] 中,下一個存儲在 b[off+1] 中,依次類推。讀取的位元組數最多等于 len。設 k 為實際讀取的位元組數;這些位元組将存儲在 b[off] 到 b[off+k-1] 的元素中,不影響 b[off+k] 到 b[off+len-1] 的元素。在任何情況下,b[0] 到 b[off] 的元素以及 b[off+len] 到 b[b.length-1] 的元素都不會受到影響。類 InputStream 的 read(b, off, len) 方法重複調用方法 read()。如果第一次這樣的調用導緻 IOException,則從對 read(b, off, len) 方法的調用中傳回該異常。如果對 read() 的任何後續調用導緻 IOException,則捕獲該異常并将其視為到達檔案末尾;到達該點時讀取的位元組存儲在 b 中,并傳回發生異常之前讀取的位元組數。在已讀取輸入資料 len 的請求數量、檢測到檔案結束标記、抛出異常前,此方法的預設實作将一直阻塞。建議子類提供此方法更為有效的實作。

4、public long skip(long n) throws IOException {...};

跳過和丢棄此輸入流中資料的 n 個位元組。出于各種原因,skip 方法結束時跳過的位元組數可能小于該數,也可能為 0。導緻這種情況的原因很多,跳過 n 個位元組之前已到達檔案末尾隻是其中一種可能。傳回跳過的實際位元組數。如果 n 為負,則不跳過任何位元組。此類的 skip 方法建立一個 byte 數組,然後重複将位元組讀入其中,直到讀夠 n 個位元組或已到達流末尾為止。建議子類提供此方法更為有效的實作。例如,可依賴搜尋能力的實作。

5、public int available() throws IOException {...};

傳回此輸入流下一個方法調用可以不受阻塞地從此輸入流讀取(或跳過)的估計位元組數,即傳回此輸入流未讀取位元組數。下一個調用可能是同一個線程,也可能是另一個線程。一次讀取或跳過此估計數個位元組不會受阻塞,但讀取或跳過的位元組數可能小于該數。注意,有些 InputStream 的實作将傳回流中的位元組總數,但也有很多實作不會這樣做。試圖使用此方法的傳回值配置設定緩沖區,以儲存此流所有資料的做法是不正确的。如果已經調用 close() 方法關閉了此輸入流,那麼此方法的子類實作可以選擇抛出 IOException。類 InputStream 的 available 方法總是傳回 0。此方法應該由子類重寫。

6、public void close() throws IOException {};

關閉此輸入流并釋放與該流關聯的所有系統資源。InputStream 的 close 方法不執行任何操作。

7、public synchronized void mark(int readlimit) {};

在此輸入流中标記目前的位置。對 reset 方法的後續調用會在最後标記的位置重新定位此流,以便後續讀取重新讀取相同的位元組。readlimit 參數告知此輸入流在标記位置失效之前允許讀取的位元組數。mark 的正常協定是:如果方法 markSupported 傳回 true,那麼輸入流總是在調用 mark 之後記錄所有讀取的位元組,并時刻準備在調用方法 reset 時(無論何時),再次提供這些相同的位元組。但是,如果在調用 reset 之前可以從流中讀取多于 readlimit 的位元組,則不需要該流記錄任何資料。标記已關閉的流對其無效。InputStream 的 mark 方法不執行任何操作。

8、public synchronized void reset() throws IOException {...};

将此流重新定位到最後一次對此輸入流調用 mark 方法時的位置。除了抛出 IOException 之外,類 InputStream 的方法 reset 不執行任何操作。

9、public boolean markSupported() {...};

測試此輸入流是否支援 mark 和 reset 方法。是否支援 mark 和 reset 是特定輸入流執行個體的不變屬性。InputStream 的 markSupported 方法傳回 false。如果此輸入流執行個體支援 mark 和 reset 方法,則傳回 true;否則傳回 false。

InputStream子類:

InputStream是一個抽象類,表示位元組輸入流的所有類的超類,其提供的大部分方法需要子類去實作,子類的所有功能也圍繞這幾個方法進行擴充實作。InputStream的直接子類有7個,分别是ByteArrayInputStream, FileInputStream, FilterInputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream

1、ByteArrayInputStream

包含一個内部緩沖區,該緩沖區包含從流中讀取的位元組。内部計數器跟蹤 read 方法要提供的下一個位元組。關閉 ByteArrayInputStream 無效。此類中的方法在關閉此流後仍可被調用,而不會産生任何 IOException。

字段以及構造方法描述,其他方法功能描述與父類InputStream一緻,不再說明。截圖自JDK API1.6文檔(下同):

java io 擴充卡_簡述JAVA IO流以及IO流中的擴充卡模式、裝飾模式

2、FileInputStream

從檔案系統中的某個檔案中獲得輸入位元組。哪些檔案可用取決于主機環境。用于讀取諸如圖像資料之類的原始位元組流。要讀取字元流,請考慮使用 FileReader。

java io 擴充卡_簡述JAVA IO流以及IO流中的擴充卡模式、裝飾模式

3、FilterInputStream

包含其他一些輸入流,它将這些流用作其基本資料源,它可以直接傳輸資料或提供一些額外的功能。FilterInputStream 類本身隻是簡單地重寫那些将所有請求傳遞給所包含輸入流的 InputStream 的所有方法。FilterInputStream 的子類可進一步重寫這些方法中的一些方法,并且還可以提供一些額外的方法和字段。FilterInputStream子類有BufferedInputStream、DataInputStream、PustbackInputStream。

java io 擴充卡_簡述JAVA IO流以及IO流中的擴充卡模式、裝飾模式

3.1、BufferedInputStream

為另一個輸入流添加一些功能,即緩沖輸入以及支援 mark 和 reset 方法的能力。在建立 BufferedInputStream 時,會建立一個内部緩沖區數組。在讀取或跳過流中的位元組時,可根據需要從包含的輸入流再次填充該内部緩沖區,一次填充多個位元組。mark 操作記錄輸入流中的某個點,reset 操作使得在從包含的輸入流中擷取新位元組之前,再次讀取自最後一次 mark 操作後讀取的所有位元組。

java io 擴充卡_簡述JAVA IO流以及IO流中的擴充卡模式、裝飾模式

3.2、DataInputStream

資料輸入流允許應用程式以與機器無關方式從底層輸入流中讀取基本 Java 資料類型。應用程式可以使用資料輸出流寫入稍後由資料輸入流讀取的資料。DataInputStream 對于多線程通路不一定是安全的。

3.3、PustbackInputStream

為另一個輸入流添加性能,即“推回 (push back)”或“取消讀取 (unread)”一個位元組的能力。在代碼片段可以很友善地讀取由特定位元組值分隔的不定數量的資料位元組時,這很有用;在讀取終止位元組後,代碼片段可以“取消讀取”該位元組,這樣,輸入流上的下一個讀取操作将會重新讀取被推回的位元組。例如,表示構成辨別符字元的位元組可能由表示操作符字元的位元組終止;用于讀取一個辨別符的方法可以讀取到遇到操作符為止,然後将該操作符推回以進行重讀。

ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream由于篇幅原因而且并不常用,這裡不再闡述,後續如有遇到會逐個介紹。

b、字元流Reader及其子類

字元流與位元組流的方法大體一緻,唯一不同的就是,讀取的時候字元流是一個一個字元的讀或者讀字元數組,而位元組流是一個一個位元組的讀或者讀位元組數組,故關于字元流的類不再詳細闡述。

c、兩個demo

位元組流demo:// 位元組流讀取檔案内容

// stream.txt檔案内容是英文數字

File file = new File("D:/work/test/stream.txt");

FileInputStream is = new FileInputStream(file);

int i;

StringBuffer sb = new StringBuffer();

// 一個位元組一個位元組的讀,如有位元組傳回該位元組的int值,讀到檔案末尾傳回-1

// while ((i = is.read()) != -1) {

// sb.append((byte) i);

// }

byte[] b = new byte[1024];

// 将資料讀到緩存數組中,每次最多讀b.length個位元組,傳回實際讀到的位元組數,如到檔案末尾傳回-1

while ((i = is.read(b)) != -1) {

sb.append(new String(b, 0, i));

}

is.close();

字元流demo:// 字元流讀取檔案内容

// 檔案包含中文

File file = new File("D:/work/test/reader.txt");

// 可指定編碼建立字元流對象

InputStreamReader reader = new InputStreamReader(new FileInputStream(file), "GBK");

StringBuffer sb = new StringBuffer();

int i;

// 一個字元一個字元的讀,如有字元傳回該位元組的int值,讀到檔案末尾傳回-1

// while ((i = reader.read()) != -1) {

// sb.append((char) i);

// }

char[] c = new char[1024];

// 将資料讀到緩存字元數組中,每次最多讀b.length個位元組,傳回實際讀到的字元數,如到檔案末尾傳回-1

while ((i = reader.read(c)) != -1) {

sb.append(c, 0, i);

}

reader.close();

四、IO流設計模式之擴充卡模式

1、擴充卡模式概念

所謂擴充卡模式就是将某個類的接口轉換為用戶端期望的另一個接口表示,其主要目的是相容性,讓原本不相幹的兩個類可以協同一起工作。

2、IO流中擴充卡模式分析

IO流中多處使用了擴充卡模式,比較典型的就是位元組流轉字元流,目标接口字元流Reader與需要被适配的類InputStream原本是兩個不相幹的類,為了滿足可以讀字元功能,有了擴充卡者InputStreamReader。InputStreamReader的作用就是将位元組輸入流轉換為能讀取字元的字元輸入流。

由InputStreamReader構造方法可知,接受一個位元組流InputStream,通過InputSream生成一個StreamDecoder對象,後續字元流讀寫都是靠這個StreamDecoder對象,間接利用了InputSream的功能。public InputStreamReader(InputStream in) {

super(in);

try {

sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object

} catch (UnsupportedEncodingException e) {

// The default encoding should always be available

throw new Error(e);

}

}

五、IO流中設計模式之裝飾模式

1、裝飾模式概念

裝飾模式指的是在不必改變原類檔案和使用繼承的情況下,動态的擴充一個對象的功能。它是通過建立一個包裝對象,也就是裝飾來包裹真實對象。裝飾模式又叫包裝(Wrapper)模式。

2、裝飾模式各角色

(1)抽象構件(component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。

(2)具體構件(Concrete Component)角色:定義一個将要接收附加責任的類。

(3)裝飾(Decorator)角色:持有一個構件(Component)對象的執行個體,并實作一個與抽象構件接口一緻的接口。

(4)具體裝飾(Concrete Decorator)角色:負責給構件對象添加上附加的責任。

注:責任可了解為功能,附加責任即為擴充的功能

3、IO流中裝飾模式分析

(1)抽象構件角色為InputStream抽象類,定義了位元組輸入流的基本方法。如read()方法public abstract int read() throws IOException;

(2)具體構件角色ByteArrayInputStream類,繼承了InputStream類,重寫了相關方法如read()方法。public synchronized int read() {

return (pos < count) ? (buf[pos++] & 0xff) : -1;

}

(3) 裝飾角色FilterInputStream類,繼承了InputSream類,持有InputStream對象,重寫了相關方法如read()方法,将方法委派給具體構件角色(ByteArrayInputStream)執行。//繼承抽象構件角色

class FilterInputStream extends InputStream {

//持有抽象構件對象執行個體

protected volatile InputStream in;

protected FilterInputStream(InputStream in) {

this.in = in;

}

public int read() throws IOException {

//委派給具體構件角色執行

return in.read();

}

}

(4)具體裝飾角色BufferedInputStream類,繼承裝飾角色FilterInputSream類,添加了相關的屬性,重寫了read()方法,增強了原有read()方法功能,提高了讀效率。class BufferedInputStream extends FilterInputStream {

private static int DEFAULT_BUFFER_SIZE = 8192;

private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

protected volatile byte buf[];

protected int count;

protected int pos;

protected int markpos = -1;

protected int marklimit;

public BufferedInputStream(InputStream in) {

this(in, DEFAULT_BUFFER_SIZE);

}

public BufferedInputStream(InputStream in, int size) {

super(in);

if (size <= 0) {

throw new IllegalArgumentException("Buffer size <= 0");

}

buf = new byte[size];

}

public synchronized int read() throws IOException {

if (pos >= count) {

fill();

if (pos >= count)

return -1;

}

return getBufIfOpen()[pos++] & 0xff;

}

}

總結:擴充卡模式是在擴充卡中,重寫舊接口的方法來調用新接口方法,來實作舊接口不改變,同僚使用新接口的目的,新接口适配舊接口。而裝飾模式,是裝飾器和舊接口實作相同的接口,在調用新接口的方法中,會調用舊接口的方法,并對其進行擴充。

本章節主要以輸入流為主,未對輸出流詳細說明。如有錯誤,歡迎指正,謝謝!