1. 程式人生 > >Java IO 之 InputStream原始碼

Java IO 之 InputStream原始碼

Writer:李強強

一、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