目錄:系統學習 Java IO ---- 目錄,概覽html
是Java IO API中全部輸入流的父類。
表示有序的字節流,換句話說,能夠將 InputStream 中的數據做爲有序的字節序列讀取。
這在從文件讀取數據或經過網絡接收時很是有用。
InputStream 一般鏈接到某些數據源,如文件,網絡鏈接,管道等
看以下代碼片斷:java
public class InputStreamExample { public static void main(String[] args) throws IOException { InputStream inputStream = new FileInputStream("D:\\out.txt"); //do something with data... int data = inputStream.read(); while (data != -1) { System.out.print((char) data); data = inputStream.read(); } inputStream.close(); } }
注意:爲了代碼清晰,這裏並無考慮處理異常的狀況,IO 異常處理有專門的介紹。算法
此方法返回的是 int 值,其中包含讀取的字節的字節值,能夠將返回的 int 強制轉換爲 char 輸出。
若是 read() 方法返回 -1 ,則表示已到達流的末尾,這意味着在 InputStream 中再也不有要讀取的數據。
也就是說,-1 做爲 int 值,而不是 -1 做爲 char 或 short,這裏有區別!數組
InputStream 類還包含兩個 read() 方法,這些方法能夠將 InputStream 源中的數據讀入字節數組。
這些方法是:緩存
一次讀取一個字節數比一次讀取一個字節要快得多,因此在能夠的時候,使用這些讀取方法而不是 read() 方法。網絡
read(byte [])方法將嘗試將盡量多的字節讀入做爲參數給出的字節數組,由於數組具備空間。
該方法返回一個 int ,其值是實際讀取了多少字節,這點和 read() 方法不同。
若是能夠從 InputStream 讀取的字節少於字節數組的空間,則字節數組的其他部分將包含與讀取開始以前相同的數據。例如:源碼分析
InputStream input = new ByteArrayInputStream("123456789".getBytes()); byte[] bytes = new byte[4]; // 每次只讀取 4 個字節 int data = input.read(bytes); while (data != -1) { System.out.print(new String(bytes)); data = input.read(bytes); }
將輸出 123456789678 ,而不是預期的 123456789 !學習
由於第一次讀取進 bytes 是 1234 ,第二次將是 5678 ,如今只剩下 9 一個數字了,注意此時 bytes 的值是 5678 ,而後再讀取剩下 1個 9,不能裝滿 bytes 了,只能覆蓋 bytes的第一個字節,最後返回的bytes 是 9678。
因此記住檢查返回的 int 以查看實際讀入字節數組的字節數。ui
int read(byte[], int offset, int length);方法和 read(byte [])方法差很少,只是增長了偏移量和指定長度。
和 read() 同樣,都是返回 -1 表示數據讀取結束。
使用實例以下:this
InputStream inputstream = new FileInputStream("D://out.txt"); byte[] data = new byte[1024]; int bytesRead = inputstream.read(data); while(bytesRead != -1) { doSomethingWithData(data, bytesRead); bytesRead = inputstream.read(data); } inputstream.close();
首先,此示例建立一個字節數組。
而後它建立一個名爲 bytesRead 的 int 變量來保存每次讀取 byte [] 調用時讀取的字節數,
並當即分配 bytesRead 從第一次讀取 byte [] 調用返回的值。
InputStream 類有兩個名爲 mark() 和 reset() 的方法,InputStream 的子類可能支持也可能不支持:
public boolean markSupported() { return false; }
也是不支持 mark( )和 reset() 方法mark() 在 InputStream 內部設置一個標記,默認值在位置 0 處。
能夠手動標記到目前爲止已讀取數據的流中的點,而後,代碼能夠繼續從 InputStream 中讀取數據。
若是想要返回到設置標記的流中的點,在 InputStream 上調用 reset() ,而後 InputStream 「倒退」並返回標記,
如此,即可再次從該mark點開始返回(讀取)數據。很明顯這可能會致使一些數據從 InputStream 返回屢次。我來舉個例子:
public static void testMarkAndReset() throws IOException { InputStream input = new ByteArrayInputStream("123456789".getBytes()); System.out.println("第一次打印:"); int count = 0;// 計算是第幾回讀取,將在第二次讀取時作標記; byte[] bytes = new byte[3]; // 每次只讀取 3 個字節 int data = input.read(bytes); while (data != -1) { System.out.print(new String(bytes)); if (++count == 2) { // 在第二輪讀取,即讀到數字 4 的時候,作標記 input.mark(16); // 從 mark 點開始再過 readlimit 個字節,mark 將失效 } data = input.read(bytes); } input.reset(); System.out.println("\n在通過 mark 和 reset 以後從 mark 位置開始打印:"); data = input.read(bytes); while (data != -1) { System.out.print(new String(bytes)); data = input.read(bytes); } }
將會輸出:
第一次打印: 123456789 在通過 mark 和 reset 以後從 mark 位置開始打印: 789
另外要說明一下 mark(int readlimit) 參數,readlimit 是告訴系統,過了這個 mark 點以後,給本宮記住日後的 readlimit 個字節,由於到時候 reset 以後,要從 mark 點開始讀取的;但實際狀況和 jdk 文檔有出入,不少狀況下調用 mark(int readlimit) 方法後,即便讀取超過 readlimit 字節的數據,mark 標記仍有效,這又是爲何呢?網上有人解答,但我仍是決定親自探索一番。
咱們這個實例引用的實際對象是 ByteArrayInputStream
先看一下它的源碼:
/* Note: The readAheadLimit for this class has no meaning.*/ public void mark(int readAheadLimit) { mark = pos; }
好傢伙,它說這個參數對於這個類沒有任何做用。
那咱們在看看其餘的 InputStream 子類,經驗證,FileInputStream 和一些實現類不支持 mark() 方法,咱們看看 BufferedInputStream
類源碼:
我先把一些字段的含義說明一下:
count
索引1大於緩衝區中最後一個有效字節的索引。 該值始終在0到buf.length的範圍內; 元素buf [0]到buf [count-1]包含從底層輸入流得到的緩衝輸入數據。在 read() 方法中讀完數據返回 -1 就是由於if (pos >= count) return -1;
pos
指緩衝區中的當前位置。 這是要從 buf 數組中讀取的下一個字符的索引。markpos
是調用最後一個 mark() 方法時 pos 字段的值。該值始終在-1到pos的範圍內。 若是輸入流中沒有標記位置,則此字段爲-1。BufferedInputStream 是每次讀取必定量的數據到 buf 數組中的,設置了 readlimit 確定是想讓數組從 mark 索引開始至少記錄到 (mark + readlimit) 索引。
public synchronized void mark(int readlimit) { marklimit = readlimit; markpos = pos; } public synchronized void reset() throws IOException { getBufIfOpen(); // Cause exception if closed if (markpos < 0) throw new IOException("Resetting to invalid mark"); pos = markpos; } private void fill() throws IOException { byte[] buffer = getBufIfOpen(); if (markpos < 0) pos = 0; /* 若是標記點不在緩衝數組裏(沒標記點),丟掉buffer,取新數據 */ else if (pos >= buffer.length) /* 緩衝區中當前位置比buffer數組大,才執行下面代碼 */ if (markpos > 0) { /* 能夠把 markpos 左邊的數據丟掉 */ int sz = pos - markpos; // 須要緩存的字節長度,從 markpos 開始 System.arraycopy(buffer, markpos, buffer, 0, sz); // 複用內存空間 pos = sz; markpos = 0; } else if (buffer.length >= marklimit) { // 若是 buffer 的長度已經大於 marklimit markpos = -1; /* 那 mark 就失效了*/ pos = 0; /* 刪除buffer內容,取新數據 */ } else if (buffer.length >= MAX_BUFFER_SIZE) { // 若是buffer過長就拋錯 throw new OutOfMemoryError("Required array size too large"); } else { /* buffer 還沒 marklimit 大,擴容到 pos 的2倍或者最大值 */ int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE; if (nsz > marklimit) nsz = marklimit; byte nbuf[] = new byte[nsz]; System.arraycopy(buffer, 0, nbuf, 0, pos); if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { throw new IOException("Stream closed"); } buffer = nbuf; } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) count = n + pos; }
能夠得出:設置標記後,
Math.min(2倍 pos 或最大值 ,marklimit);
咱們就只分析了 ByteArrayInputStream 和 BufferedInputSteam 類的算法,其它輸入流不知道。所以 mark() 方法標記時,務必考慮好 readlimit 的值。
OutputStream 一般始終鏈接到某個數據目標,如文件,網絡鏈接,管道等。 OutputStream 的目標是將數據寫入到外部。
write(byte) 方法用於將單個字節寫入 OutputStream。 OutputStream 的 write() 方法接受一個 int ,其中包含要寫入的字節的字節值。 只寫入 int 值的第一個字節。 其他的被忽略了。
OutputStream 的子類有重寫的 write() 方法。 例如,DataOutputStream 容許使用相應的方法writeBoolean(),writeDouble() 等編寫諸如 int,long,float,double,boolean 等 Java 基本類型。
和 InputStream 同樣,它們也能夠將一個數組或一部分字節寫入 OutputStream 。
OutputStream 的flush() 方法將寫入 OutputStream 的全部數據刷新到底層數據目標。 例如,若是 OutputStream 是 FileOutputStream ,則寫入 FileOutputStream 的字節可能還沒有徹底寫入磁盤。 即便您的代碼已將其寫入 FileOutputStream ,但數據也可能還在某處緩存在內存中。 經過調用 flush() 能夠確保將任何緩衝的數據刷新(寫入)到磁盤(或網絡,或 OutputStream 的目標)。