Java IO 之 InputStream源碼
一、InputStream
InputStream是一個抽象類,即表示所有字節輸入流實現類的基類。它的作用就是抽象地表示所有從不同數據源產生輸入的類,例如常見的FileInputStream、FilterInputStream等。那些數據源呢?比如:
1) 字節數組(不代表String類,但可以轉換)
2) String對象
3) 文件
4) 一個其他種類的流組成的序列化 (在分布式係統中常見)
5) 管道(多線程環境中的數據源)
等等
二者,注意它是屬於字節流部分,而不是字符流(java.io中Reader\Writer,下麵會講到)。
FilterInputStream是為各種InputStream實現類提供的“裝飾器模式”的基類。因此,可以分為原始的字節流和“裝飾”過的功能封裝字節流。
二、細解InputStream源碼的核心
源碼如下:
/**
* 所有字節輸入流實現類的基類
*/
public abstract class SInputStream {
// 緩存區字節數組最大值
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
// 從輸入流中讀取數據的下一個字節,以int返回
public abstract int read() throws IOException;
// 從輸入流中讀取數據的一定數量字節,並存儲在緩存數組b
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
// 從輸入流中讀取數據最多len個字節,並存儲在緩存數組b
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();
if (c == -1) {
return -1;
}
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;
}
// 跳過輸入流中數據的n個字節
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
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;
}
// 返回下一個方法調用能不受阻塞地從此讀取(或者跳過)的估計字節數
public int available() throws IOException {
return 0;
}
// 關閉此輸入流,並釋放與其關聯的所有資源
public void close() throws IOException {}
// 在此輸出流中標記當前位置
public synchronized void mark(int readlimit) {}
// 將此流重新定位到最後一次對此輸入流調用 mark 方法時的位置。
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
// 測試此輸入流是否支持 mark 和 reset 方法
public boolean markSupported() {
return false;
}
}
其中,InputStream下麵三個read方法才是核心方法:
public abstract int read()
抽象方法,沒有具體實現。因為子類必須實現此方法的一個實現。這就是輸入流的關鍵方法。
二者,可見下麵兩個read()方法都調用了這個方法子類的實現來完成功能的。
public int read(byte b[])
該方法是表示從輸入流中讀取數據的一定數量字節,並存儲在緩存字節數組b。其效果等同於調用了下麵方法的實現:
read(b, 0, b.length)
如果b的長度為 0,則不讀取任何字節並返回 0;否則,嚐試讀取至少 1 字節。如果因為流位於文件末尾而沒有可用的字節,則返回值 -1;否則,至少讀取一個字節並將其存儲在 b 中。
思考:這時候,怪不得很多時候, b != –1 或者 b != EOF
public int read(byte b[], int off, int len)
在輸入數據可用、檢測到流末尾或者拋出異常前,此方法一直阻塞。
該方法先進行校驗,然後校驗下個字節是否為空。如果校驗通過後,
如下代碼:
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
將讀取的第一個字節存儲在元素 b[off] 中,下一個存儲在 b[off+1] 中,依次類推。讀取的字節數最多等於 len。設 k 為實際讀取的字節數;這些字節將存儲在 b[off] 到 b[off+k-1] 的元素中,不影響 b[off+k] 到 b[off+len-1] 的元素。
因為有上麵兩個read的實現,所以這裏InputStream設計為抽象類。
三、小結
1. InputSream 對應著 OutputStream
2. 看源碼是享受人家寫代碼中流露的How
3. 泥瓦匠學習的代碼都在github上(同步osc git),歡迎大家點star,提意見,一起進步。地址:https://github.com/JeffLi1993
最後更新:2017-05-22 13:01:23