InputStream與OutPutStream兩個抽象類,是所有的流的基礎,首先來看這兩個流的API
InputStream:
public abstract int read() throws IOException; | 從輸入流中讀取數據的下個字節。 |
public int read(byte b[]) throws IOException{…} | 從輸入流中讀取一定數量的字節,並將其存儲在緩衝區數組b中。 |
public int read(byte b[], int off, int len) throws IOException{…} | 將輸入流中最多len個數據字節讀入byte數組。 |
public long skip(long n) throws IOException{…} | 跳過和丟棄此輸入流中數據的n個字節。 |
public int available() throws IOException{…} | 返回此輸入流下一個方法調用可以不受阻塞地從此輸入流讀取(或跳過)的估計字節數。 |
public void close() throws IOException {} | 關閉此輸入流並釋放與該流關聯的所有系統資源。 |
public synchronized void mark(int readlimit) {} | 在此輸入流中標記當前的位置。對reset方法的後續調用會在最後標記的位置重新定位此流,以便後續讀取重新讀取相同的字節。 |
public synchronized void reset() throws IOException {…} | 將此流重新定位到最後一次對此輸入流調用mark方法時的位置。 |
public boolean markSupported() {…} | 測試此輸入流是否支持mark和reset方法。 |
OutPutStream
方法 | 說明 |
---|---|
public abstract void write(int b) throws IOException; | 將指定的字節寫入此輸出流。 |
public void write(byte b[]) throws IOException {…} | 將b.length個字節從指定的byte數組寫入此輸出流。 |
public void write(byte b[], int off, int len) throws IOException {…} | 將指定byte數組中從偏移量off開始的len個字節寫入此輸出流。 |
public void flush() throws IOException {} | 刷新此輸出流並強制寫出所有緩衝的輸出字節。 |
public void close() throws IOException {} | 關閉此輸出流並釋放與此流有關的所有系統資源。 |
這兩個類的方法如上,可以看到在這兩個方法中,分別具有一個抽象方法,分別爲read()和write()方法。這兩個方法便是整個IO體系中的核心方法,所有的內容均由這兩個方法爲基礎進行實現。然後我們來看一下主要的兩個比較主要的read(byte b[], int off, int len) 方法和write(byte b[], int off, int len)方法的實現:
/**
* 將輸入流中最多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的請求數量、檢測到文件結束標記、拋出異常前,此方法的默認實現將一直阻塞。建議子類提供此方法更爲有效的實現。
*
* @param b 讀入數據的緩衝區。
* @param off 數組 b 中將寫入數據的初始偏移量。
* @param len 要讀取的最大字節數。
* @return 讀入緩衝區的總字節數;如果因爲已到達流末尾而不再有數據可用,則返回-1。
* @exception IOException 如果不是因爲位於文件末尾而無法讀取第一個字節;如果輸入流已關閉;如果發生其他 I/O 錯誤。
* @exception NullPointerException 如果 b 爲 null。
* @exception IndexOutOfBoundsException 如果off爲負,len爲負,或者len大於b.length - off
* @see java.io.InputStream#read()
*/
public int read(byte b[], int off, int len) throws IOException {
//檢查參數是否合法,不合法拋出異常
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
// 讀取一個字節
int c = read();
// 如果因爲流位於文件末尾而沒有可用的字節,返回-1
if (c == -1) {
return -1;
}
// 將讀取的第一個字節存儲在元素b[off]中
b[off] = (byte)c;
int i = 1;
// 繼續往下讀取
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
/**
* 將指定byte數組中從偏移量off開始的len個字節寫入此輸出流。
*
* write(b, off, len) 的常規協定是:
* 將數組b中的某些字節按順序寫入輸出流;
* 元素b[off]是此操作寫入的第一個字節,b[off+len-1]是此操作寫入的最後一個字節。
*
* OutputStream 的write方法對每個要寫出的字節調用一個參數的write 方法。
* 建議子類重寫此方法並提供更有效的實現。
*
* @param b 數據。
* @param off 數據中的初始偏移量。
* @param len 寫入的字節數。
* @exception IOException 如果發生I/O錯誤。尤其是關閉了輸出流。
*/
public void write(byte b[], int off, int len) throws IOException {
//檢查參數是否合法
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
// 將指定byte數組中從偏移量off開始的len個字節寫入此輸出流。
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
從上方兩個代碼片段中,可以看出在進行讀取流時,操作時非常簡單的,只是調用write方法與read方法進行處理流信息。分別爲將read出來的字節,寫入到byte[]的某位中,將一個byte[]字節中的某位,寫入到流中。
此時,我們再看一段比較有意思的代碼,這段代碼是InputStream中的skip()方法:
/**
* 跳過和丟棄此輸入流中數據的n個字節。出於各種原因,skip方法結束時跳過的字節數可能小於該數,也可能爲0。導致這種情況的原因很多,跳過n個字節之前已到達文件末尾只是其中一種可能。返回跳過的實際字節數。如果n爲負,方法返回0,,不跳過任何字節。子類可能對負值有不一樣的處理。
*
* skip方法創建一個byte數組,然後重複將字節讀入其中,直到讀夠n個字節或已到達流末尾爲止。建議子類提供此方法更爲有效的實現。例如,可依賴搜索能力的實現。
*
* @param n 要跳過的字節數。
* @return 實際跳過的字節數
* @exception IOException 如果流不支持搜索,或者發生其他 I/O 錯誤。
*/
public long skip(long n) throws IOException {
long remaining = n;
int nr;
// 如果n<=0,返回0
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
// 創建一個byte數組,然後重複將字節讀入其中,直到讀夠n個字節或已到達流末尾爲止。
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
這個方法的用途爲,將接下來的字節丟棄,在這個方法中,進行的操作爲新創建一個空的byte[],然後將這N個字節的信息,讀取到這個byte[]中,然後將這個byte[]棄之一邊,等待GC進行回收。
然後,我們要注意一下的是,默認InputStream與OutPutStream的源碼中,close()方法的實現都是空的,包括輸出流中的close()方法。
總結:在InputStream與OutPutStream中,爲我們提供了一些基本的write和read的方法的封裝,但是這些方法都是依賴於抽象的read()方法與write()方法的。同時,還可以注意到,在進行InputStream的讀取時,如果想要跳過若干個字節,這裏的處理實際上是對這若干個字節進行了讀取,只是沒有返回給上層方法,沒有對允許這部分的字節進行任何的操作,直接是在等待GC進行回收了。