天天看點

Java的IO系統--位元組流的兩個頂級父類兩個頂級父類

這篇來說說位元組流.

兩個頂級父類

位元組流有兩個頂級父類:輸入流InputStream與輸出流OutputStream,兩者均為抽象類.

InputStream

這是位元組輸入流的頂級父類,下面來分析一下它的源碼.

構造器

沒有顯式地寫出構造器,證明隻有一個預設構造器.

方法

一共有三個read():

有一個無參的抽象方法read(),是留給子類去實作的.

public abstract int read() throws IOException;
           

第二個read()傳入一個位元組數組,是利用第三個read()實作的,這裡直接看看第三read()的代碼:

public int read(byte b[]) throws IOException {
        return read(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException(); 
            //沒有byte[]就抛出空指針異常.
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException(); 
            //偏移量或要讀取的長度小于0,或要數組的長度減偏移量大于要讀取的長度就抛出下标越界異常.
        } else if (len == 0) {
            return 0;
            //要讀取的長度為0就傳回0.
        }

        int c = read(); //調用子類實作的read().
        if (c == -1) {
            return -1; //子類read()傳回-1(即沒有讀取到資料),這裡也傳回-1.
        }
        b[off] = (byte)c; 
        //把讀取到的第一個位元組存入byte[]的下标為off(偏移量)處.
        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read(); //讀取  
                if (c == -1) {
                    break; //傳回-1就停止.
                }
                b[off + i] = (byte)c; //存入.
            }
        } catch (IOException ee) {
        }
        return i; //傳回讀取了幾個位元組.
    }
           

三個read()的共同特點是,每次都是讀取一個位元組,利用for循環進行連續讀取.當發現沒有讀取到位元組的時候,就傳回-1,裡面for循環中的i,每讀取一個位元組就循環一次,自己也就+1,是以最終傳回的是讀取到的位元組數.

然後是skip(),補上方法内涉及的那個常量,作用是跳過位元組數(n)進行讀取檔案,傳回值根據測試就是傳入的參數.

private static final int MAX_SKIP_BUFFER_SIZE = 2048;

    public long skip(long n) throws IOException {

        long remaining = n;
        int nr;

        if (n <= 0) {
            return 0; //傳入的數值小于0就傳回0.
        }
 
        int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); //傳入的參數與常量取最小值,最大值是2048.
        byte[] skipBuffer = new byte[size]; //建立size大小的位元組數組,最大就是常量大小.
        while (remaining > 0) {
            nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); //read()
            if (nr < 0) {
                break;  //傳回值小于0(也許隻有-1?)就跳出循環.
            }
            remaining -= nr;
        }

        return n - remaining; //最後傳回的實際上就是傳入的位元組數
    }
           

寫了下面的方法測試skip()的傳回值:,檔案大小32767位元組:

public static void main(String[] args) {
        InputStream in = null;
        int nums = 0;
        try {
            in = new FileInputStream("1.txt");
            System.out.println("skip()傳回值:" + in.skip(16384));
            for (int i = 0; i < 1000; i++) {
                in.read();
                nums ++;
            }
            System.out.println("read實際讀取的位元組數:" + nums);
            nums = 0;
            System.out.println("skip()傳回值:" + in.skip(-16384));
            while (in.read() > 0) {
                nums ++;
            }
            System.out.println("read實際讀取的位元組數:" + nums);
        } catch (IOException e) {
            e.getStackTrace();
        }
    }
           

運作結果: 

skip()傳回值:16384
read實際讀取的位元組數:1000
skip()傳回值:-16384
read實際讀取的位元組數:31767
           

最開始調用skip()并傳入16384,代表跳過了16384位元組,for循環1000次,就讀取到了1000位元組.然後調用skip()并傳入負數,發現實際的讀取結果是32768-16384-1000+16384 = 31767.

就這些代碼來說,skip()相當于輸入流中的一個"指針",傳入正數就往前跳正數個位元組,然後開始讀取1000個位元組,讀取的時候指針向後移動1000,,然後skip()傳入負值,指針往回跳負數的絕對值個位元組,繼續讀取到最後,就得到了31767這個傳回值.

剩下的方法都沒有實際的邏輯或邏輯很簡單,下面簡單介紹一下它們的含義:

//這個位元組流中還剩多少個位元組.
    public int available() throws IOException {
        return 0;
    }
    
    //關閉這個流,實作Closeable接口的方法.
    public void close() throws IOException {}

    //在流中标記readLimit位置
    public synchronized void mark(int readlimit) {}

    //抛出IO異常:不支援mark或reset操作
    public synchronized void reset() throws IOException {
        throw new IOException("mark/reset not supported");
    }

    //測試這個輸入流是否支援 mark()和 reset(). 
    public boolean markSupported() {
        return false;
    }
           

OutputStream

位元組輸出流的頂級父類,這個類裡面的方法比InputStream中的少.

構造器

依舊是沒有顯式提供構造器.

方法

三個write(),其中有一個抽象方法,依舊是留給子類重寫的,不過相比輸入流參數多變成了一個int值:

public abstract void write(int b) throws IOException;
           

第二個是調用本類中給出具體實作的write(),直接來看給出具體實作的那個write(): 

public void write(byte b[]) throws IOException {
        write(b, 0, b.length);
    }

    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();
            //偏移量小于0,或大于位元組數組容量,要寫入的長度小于0,
            //偏移量加要寫入的長度大于位元組數組容量或小于0,就抛出下标越界異常.
        } else if (len == 0) {
            return; //資料全部寫完,退出方法.
        }
        for (int i = 0 ; i < len ; i++) {
            write(b[off + i]); //利用子類實作的write()開始寫入.
        }
    }
           

可以看到,與read()傳回int不同,write()沒有傳回值,這點需要注意. 

還剩下兩個沒有具體實作的方法:

//關閉這個流,實作Closeable接口的方法.
    public void flush() throws IOException {}
    //将剩餘的資料沖刷出去,實作Flushable的方法.
    public void close() throws IOException {}