天天看點

從JDK源碼看InputStream

JDK 給我們提供了很多實用的輸入流 xxxInputStream,而 InputStream 是所有位元組輸入流的抽象。包括 ByteArrayInputStream 、FilterInputStream 、BufferedInputStream 、DataInputStream 和 PushbackInputStream 等等。

InputStream 被定為 public 且 abstract 的類,實作了Closeable接口。

Closeable 接口表示 InputStream 可以被close,接口定義如下:

MAX_SKIP_BUFFER_SIZE 表示輸入流每次最多能跳過的位元組數。

DEFAULT_BUFFER_SIZE 預設的緩沖大小。

MAX_BUFFER_SIZE 表示最大的緩沖數組大小,這裡設定為 Integer.MAX_VALUE - 8 這裡也是考慮到 JVM 能支援的大小,超過這個值就會導緻 OutOfMemoryError。

一共有三個 read 方法,其中有一個抽象的 read 方法,其餘兩個 read 方法都會調用這個抽象方法,該方法用于從輸入流讀取下一個位元組,傳回一個0到255範圍的值。如果已經到達輸入流結尾處而導緻無可讀位元組則傳回-1,同時,此方法為阻塞方法,解除阻塞的條件:

1. 有可讀的位元組。

2. 檢測到已經是輸入流的結尾了。

3. 抛出異常。

主要看第三個 read 方法即可,它傳入的三個參數,byte數組、偏移量和數組長度。該方法主要是從輸入流中讀取指定長度的位元組資料到位元組數組中,需要注意的是這裡隻是嘗試去讀取長度為 len 的數組,但真正讀取到的數組長度不一定為 len,傳回值才是真正讀取到的長度。

看看它的邏輯,數組為null則抛空指針,偏移量和長度超過邊界也抛異常,長度為0則什麼都不敢直接傳回0。接着調用 read() 讀取一個位元組,如果為-1則說明結束,直接傳回-1。否則繼續根據數組長度循環調用 read() 方法讀取位元組,并且填充到傳入的數組對象中,最後傳回讀取的位元組數。

該方法從輸入流讀取所有剩餘的位元組,在此過程是阻塞的,直到所有剩餘位元組都被讀取或到達流的結尾或發生異常。

邏輯是用一個 for 循環内嵌一個 while 循環,while 循環不斷調用 read 方法嘗試将 DEFAULT_BUFFER_SIZE 長度的位元組數組填滿,一旦填滿則需要将數組容量擴容一倍,再将原位元組數組複制到新數組中,然後再通過 while 循環繼續讀取,直到達到尾部才跳出 for 循環,最後傳回讀取到的所有位元組數組。

從輸入流中讀取指定長度的位元組,而且它能保證一定能讀取到指定的長度,它屬于阻塞方式,用一個 while 循環不斷調用 read 讀取位元組,直到讀取到指定長度才結束讀取。

傳回從該輸入流能進行非阻塞讀取的剩餘位元組數,當調用 read 讀取的位元組數一般會小于該值,有一些InputStream的子實作類會通過該方法傳回流的剩餘總位元組數,但有些并不會,是以使用時要注意點。

這裡抽象類直接傳回0,子類中重寫該方法。

從輸入流中跳過指定個數位元組,傳回值為真正跳過的個數。這裡的實作是簡單通過不斷調用 read 方法來實作跳過邏輯,但這是較低效的,子類可用更高效的方式重寫此方法。

下面看看邏輯,最大的跳過長度不能超過 MAX_SKIP_BUFFER_SIZE ,并且用一個 while 循環調用 read 方法,如果遇到傳回為-1,即已經到達結尾了,則跳出循環。可以看到 skipBuffer 其實是沒有什麼作用,直接讓其被 GC 即可,最後傳回真正跳過的位元組數。

此方法用于關閉輸入流,并且釋放相關資源 。

從輸入流中按順序讀取全部位元組并且寫入到指定的輸出流中,傳回值為轉移的位元組數。轉移過程中可能會發生不确定次的阻塞,阻塞可能發生在 read 操作或 write 操作。

主要邏輯是用 while 循環不斷調用 read 方法操作讀取位元組,然後調用輸出流的 write 方法寫入,直到讀取傳回-1,即達到結尾。最後傳回轉移的位元組數。

是否支援 mark 和 reset 操作,這裡直接傳回 false,子類根據實際重寫該方法。

标記輸入流目前位置,與之對應的是 reset 方法,通過他們之間的組合能實作重複讀取操作。另外它會傳入 readlimit 參數,它用于表示該輸入流中在執行 mark 操作後最多可以讀 readlimit 個位元組後才使 mark 的位置失效。

可以看到 InputStream 的 mark 方法是什麼都不做的,子類中再具體實作。

與 mark 方法對應,它可以重置輸入流的位置到上次被 mark 操作辨別的位置。InputStream 的 reset 方法直接抛出一個 IOException,子類中根據實際情況實作。

以下是廣告和相關閱讀

========廣告時間========

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74080321">為什麼寫《Tomcat核心設計剖析》</a>

=========================

相關閱讀:

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73743876">從JDK源碼角度看Object</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/78026810">從JDK源碼角度看Long</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/77196626">從JDK源碼角度看Integer</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/77941129">volatile足以保證資料同步嗎</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/72933108">談談Java基礎資料類型</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51455094">從JDK源碼角度看并發鎖的優化</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51468764">從JDK源碼角度看線程的阻塞和喚醒</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51433204">從JDK源碼角度看并發競争的逾時</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51397266">從JDK源碼角度看java并發線程的中斷</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51371416">從JDK源碼角度看Java并發的公平性</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/51360228">從JDK源碼角度看java并發的原子性如何保證</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/78389889">從JDK源碼看Writer</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/78315757">從JDK源碼看關閉鈎子</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74557125">從JDK源碼角度看Byte</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/73350488">從JDK源碼角度看Boolean</a>

<a href="http://blog.csdn.net/wangyangzhizhou/article/details/76557578">從JDK源碼角度看Short</a>

歡迎關注:

從JDK源碼看InputStream
從JDK源碼看InputStream