天天看點

InputStream抽象類的方法詳解1.方法available2. 讀入方法:read3. skip方法4.與标記相關的方法

首先類的結構

InputStream抽象類的方法詳解1.方法available2. 讀入方法:read3. skip方法4.與标記相關的方法

InputStream這個抽象類是所有基于位元組的輸入流的超類

首先這是一個抽象類,實作了Closeable接口,也Closeable接口又拓展了AutoCloseable接口,是以所有InputStream及其子類都可以用于Java 7 新引入的帶資源的try語句。讀入位元組之前,我們可能想要先知道還有多少資料可用,這有available方法完成,具體的讀入由read()及其重載方法完成,skip方法用于跳過某些位元組,同時定義了幾個有關标記(mark)的方法,讀完資料使用close方法關閉流,釋放資源。下面詳細介紹各個方法:

類:

1.方法available

public int available() throws IOException
           

這個方法可以在讀寫操作前先得知資料流裡有多少個位元組可以讀取。需要注意的是,如果這個方法用在從本地檔案讀取資料時,一般不會遇到問題,但如果是用于網絡操作,就經常會遇到一些麻煩。比如,Socket通訊時,對方明明發來了1000個位元組,但是自己的程式調用available()方法卻隻得到900,或者100,甚至是0,感覺有點莫名其妙,怎麼也找不到原因。其實,這是因為網絡通訊往往是間斷性的,一串位元組往往分幾批進行發送。本地程式調用available()方法有時得到0,這可能是對方還沒有響應,也可能是對方已經響應了,但是資料還沒有送達本地。對方發送了1000個位元組給你,也許分成3批到達,這你就要調用3次available()方法才能将資料總數全部得到。

能否使用取決于實作了InputStream這個抽象類的具體子類中有沒有實作available這個方法。如果實作了那麼就可以取得大小,如果沒有實作那麼就擷取不到。例如FileInputStream就實作了available方法,那麼就可以用new byte[in.available()];這種方式。但是,網絡程式設計的時候Socket中取到的InputStream,就沒有實作這個方法,那麼就不可以使用這種方式建立數組。
           

Java異常機制很重要的一個點,子類的方法不能throws父類方法沒有throws的異常(構造器除外),是以在父類方法先指出,然後允許子類方法抛出IOException

2. 讀入方法:read

跟讀入相關的方法是這個類的核心方法。有3種重載的形式,下面分别介紹。

2.1 read()

public abstract int read() throws IOException;
           

讀取輸入流的下一個位元組。這是一個抽象方法,不提供實作,子類必須實作這個方法。該方法讀取下一個位元組,傳回一個0-255之間的int類型整數。如果到達流的末端,傳回-1。

例如:

public static void main(String[] args) {
        try (InputStream in = new FileInputStream("E:\\q.txt")) {
            int i;
            while ((i = in.read()) != -1) {
                System.out.println(i);
                System.out.println("out: " + (char) i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
           

輸出:

InputStream抽象類的方法詳解1.方法available2. 讀入方法:read3. skip方法4.與标記相關的方法

面向位元組的操作時,可能需要像這樣比較底層的位元組操作。我們也可以一次讀入多個位元組,使用下面的重載形式。

2.2 read(byte[] b)

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

試圖讀入多個位元組,存入位元組數組b,傳回實際讀入的位元組數。

如果傳遞的是一個空數組(注意數組長度可以為0,即空數組。比如 byte[] b = new byte[0]; 或者byte[] b = {};)那麼什麼也沒讀入,傳回0.

如果到達流尾部,沒有位元組可讀,傳回-1;

例如:

public static void main(String[] args) {
        try (InputStream in = new FileInputStream("E:\\q.txt")) {
            byte[] buffer = new byte[1024];
            StringBuilder sb = new StringBuilder();
            while (in.read(buffer) != -1) {
                sb.append(new String(buffer, StandardCharsets.UTF_8));
            }
            System.out.println(sb);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
           

2.3 read (byte[] b, int off, int len)

這個方法跟上一個功能類似,除了讀入的資料存儲到b數組是從off開始。len是試圖讀入的位元組數,傳回的是實際讀入的位元組數。

如果len=0,則什麼也不讀入,傳回0;如果遇到流尾部,傳回-1.否則至少讀入一個位元組。

這個方法不做過多介紹和上面方法一樣,看源碼就知道上一個調取的就是這個方法。最後看一下這個方法源碼:

public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {   // 檢測參數是否為null
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException(); // 數組越界檢測
        } else if (len == 0) {
            return 0;   //如果b為空數組,傳回0
        }
 
        int c = read(); // 調用read()方法擷取下一個位元組
        if (c == -1) {
            return -1;
        }               // 遇到流尾部,傳回-1
        b[off] = (byte)c;  //讀入的第一個位元組存入b[off]
 
        int i = 1;    // 統計實際讀入的位元組數
        try {
            for (; i < len ; i++) { // 循環調用read,直到流尾部
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c; // 一次存入位元組數組
            }
        } catch (IOException ee) {
        }
        return i;  // 傳回實際讀入的位元組數
    }
           

3. skip方法

public long skip(long n) throws IOException
           

這個方法試圖跳過目前流的n個位元組,傳回實際跳過的位元組數。如果n為負數,傳回0.當然子類可能提供不能的處理方式。n隻是我們的期望,至于具體跳過幾個,則不受我們控制,比如遇到流結尾。

InputStream抽象類的方法詳解1.方法available2. 讀入方法:read3. skip方法4.與标記相關的方法

輸出:

InputStream抽象類的方法詳解1.方法available2. 讀入方法:read3. skip方法4.與标記相關的方法

4.與标記相關的方法

4.1 mark 

public void mark(int readlimit)
           

這個方法用于在流的目前位置做個标記,參數readLimit指定這個标記的“有效期“,如果從标記處開始往後,已經擷取或者跳過了readLimit個位元組,那麼這個标記失效,不允許再重新回到這個位置(通過reset方法)。也就是你想回頭不能走得太遠呀,浪子回頭不一定是岸了,跳過(擷取)了太多位元組,标記就不再等你啦。多次調用這個方法,前面的标記會被覆寫。

InputStream抽象類的方法詳解1.方法available2. 讀入方法:read3. skip方法4.與标記相關的方法

看一下上面的圖,如果我們在M出做标記,readLimit為綠色部分,當流的指針在A處的時候,這個标記依然有效,可是一旦指針跑到B處,标記就失效了。

4.2 reset 

public void reset() throws IOException
           

這個方法用于重定位到最近的标記。如果在這之前mark方法從來沒被調用,或者标記已經無效,在抛出IOException。如果沒有抛出這個異常,将目前位置重新定位到最近的标記位置。

InputStream is = null;
		//byte[] buffer = new byte[6];
		//char c;
		
		try {
			is = new BufferedInputStream(new FileInputStream("test.txt"));
			is.mark(4);
			is.skip(2);
			is.reset();
			//is.read(buffer, 1, 3);
			System.out.println((char)is.read());
			
			/*for (byte b : buffer) {
				System.out.println((char)b);
			}*/
			
	
		} finally {
			if (is != null) {
				is.close();
			}
		}
 
	}
           

我們使用了支援mark的BufferedInputStream,首先一開始做标記,跳過兩個自己,然後再回到最初的位置。

4.3 markSupported

public boolean markSupported()
           

檢測目前流對象是否支援标記。是傳回true。否則傳回false。比如InputStream不支援标記,而BufferedInputStream支援。

5. close方法 

public void close() throws IOException
           

關閉目前流,釋放與該流相關的資源,防止資源洩露。在帶資源的try語句中将被自動調用。關閉流之後還試圖讀取位元組,會出現IOException異常。