走進JDK(四)------InputStream、OutputStream、Reader、Writer

InputStream

InputStream是java中的輸入流,下面基於java8來分析下InputStream源碼java

1、類定義

public abstract class InputStream implements Closeable

Closeable接口定義了close()方法,流在使用完以後須要關閉,而且放在finally塊中操做比較好。數組

 

2、變量

// 該變量用於肯定在skip方法中使用的最大緩存數組大小。
private static final int MAX_SKIP_BUFFER_SIZE = 2048;

 

3、主要方法

一、read()

//從輸入流中讀取下一字節數據。返回的字節值爲一個範圍在0-255之間的int數。若因爲到達流的尾部而沒有字節可獲取,則返回-1.直到數據可達,檢測到流的末尾或者拋出一個異常,該方法才中止。由每一個子類本身實現。
public abstract int read() throws IOException; //將數據放入到字節數組中,內部調用的也是read(b, 0, b.length)
public int read(byte b[]) throws IOException { return read(b, 0, b.length); } //byte[] b表明存放數據的字節數組,off表明將讀取的數據寫入數組b的起始偏移地址。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; } //這地方有個頗有意思的問題,爲啥不用byte做爲接收值,須要int,此方法的返回值的範圍爲0-255,若是使用byte,則byte會將255轉成-1,這樣就會與read()方法的約定混淆,由於返回-1時,會認爲流中已經沒有數據,但此時是有數據的。 //那麼java內部byte是怎麼轉成int的呢?例如對於-1這個值,在java中,負數都是以補碼的形式存在(能夠參考本人另一篇文章)。-1的補碼就是11111111,由於byte是1個字節8位。而int是4個字節32位,當byte->int時,全部的高位都會補0, //-1對應的int則爲00000000 00000000 00000000 11111111,這個數在int中則爲255,也就是說byte的-1轉成就是int的255.因此在下面(byte)c就能夠將數據還原成byte,這種作法既能夠保證讀取到數據爲128-255時不會出錯,也能保證byte->int->byte //讀取的是原始數據
        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; }

 

 二、available()

能夠在讀寫操做前先得知數據流裏有多少個字節能夠讀取。須要注意的是,若是這個方法用在從本地文件讀取數據時,通常不會遇到問題,但若是是用於網絡操做,就常常會遇到一些麻煩。好比,Socket通信時,對方明明發來了1000個字節,
可是本身的程序調用available()方法卻只獲得900,或者100,甚至是0,感受有點莫名其妙,怎麼也找不到緣由。其實,這是由於網絡通信每每是間斷性的,一串字節每每分幾批進行發送。
本地程序調用available()方法有時獲得0,這多是對方尚未響應,也多是對方已經響應了,可是數據尚未送達本地。對方發送了1000個字節給你,也許分紅3批到達,這你就要調用3次available()方法才能將數據總數所有獲得。
InputStream中的此方法一直返回的0,可否使用取決於實現了InputStream這個抽象類的具體子類中有沒有實現available這個方法。緩存

public int available() throws IOException { return 0; }

不少小夥伴在讀取流以前喜歡使用available()方法來判斷有多少字節,寫法以下:網絡

int count = in.available(); byte[] b = new byte[count]; in.read(b);

這樣在網絡延遲的狀況下就會有問題,應改爲以下這種(不過這種也有問題,當流沒數據一直循環):app

int count = 0; while (count == 0) { count = in.available(); } byte[] b = new byte[count]; in.read(b);

 

三、close()

//關閉輸入流並釋放與其相關的系統資源。通常放在finally中操做
public void close() throws IOException {}

 

OutputStream

 1、類定義

public abstract class OutputStream implements Closeable, Flushable

flushable接口則定義flush()函數

 

2、主要方法

一、write()

//將指定的一個字節寫入此輸出流。int 值爲 4 個字節,此方法丟棄 int 類型高位的 3 個字節,只保留低位的 1 個字節寫入(對字節來講,轉爲 int 其 3 個高位字節都是全 0 的,因此能夠丟棄)。此方法是抽象方法,子類必需要進行實現
public abstract void write(int b) throws IOException; //將 b.length 個字節從指定的 byte 數組寫入此輸出流。調用下邊的 write 方法。
public void write(byte b[]) throws IOException { write(b, 0, b.length); } //將指定 byte 數組中從偏移量 off 開始的 len 個字節寫入此輸出流。
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(); } else if (len == 0) { return; } for (int i = 0 ; i < len ; i++) { write(b[off + i]); } }

 

二、flush()

//刷新此輸出流並強制寫出全部緩衝的輸出字節。此類未實現具體行爲,子類應該複寫此方法。
public void flush() throws IOException {}

 

三、close()

//關閉此輸出流並釋放與此流有關的全部系統資源,此類未實現具體行爲,子類應該複寫此方法。
public void close() throws IOException {}

 

Reader

1、類定義

public abstract class Reader implements Readable, Closeable

Readable:Readable 接口表示嘗試將字符讀取到指定的緩衝中。this

Closeable:Closeable 接口表示 Reader 能夠被close。spa

 

2、變量

//最大跳過緩衝的大小
private static final int maxSkipBufferSize = 8192; //是一個 char[] 類型,表示跳過緩衝
private char skipBuffer[] = null; //是 Reader 的鎖,用於實現同步
protected Object lock;

 

3、構造函數

protected Reader() { this.lock = this; } protected Reader(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; }

 

4、主要方法

一、read()

//全部 read 方法最終都會調用這個抽象方法,提供給子類處理邏輯的實現。它傳入的三個參數,字符數組cbuf、偏移量off和數組長度。
public abstract int read(char cbuf[], int off, int len) throws IOException; //無參的 read 方法實際上是默認讀一個字符,new 一個 char 對象而後調用子類實現進行讀取,最後返回讀到的字符
public int read() throws IOException { char cb[] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else
        return cb[0]; } //假如 read 方法傳入的參數爲 char 數組時,則直接調用子類實現進行讀取
public int read(char cbuf[]) throws IOException { return read(cbuf, 0, cbuf.length); } //最後一個 read 方法實際上是 Readable 接口定義的方法,用於讀取字符到指定的 CharBuffer 對象中,邏輯是先獲得 CharBuffer 對象剩餘長度,根據該長度實例化 char 數組,而後調用子類實現完成讀取,最後將讀取到的字符放進 CharBuffer 對象
public int read(java.nio.CharBuffer target) throws IOException { int len = target.remaining(); char[] cbuf = new char[len]; int n = read(cbuf, 0, len); if (n > 0) target.put(cbuf, 0, n); return n; }

 

二、close()

abstract public void close() throws IOException;

 

Writer

 1、類定義

public abstract class Writer implements Appendable, Closeable, Flushable

 

2、變量

//字符緩存數組。用於臨時存放要寫入字符輸出流中的字符
private char[] writeBuffer; //字符緩存數組的默認大小
private static final int WRITE_BUFFER_SIZE = 1024; //用於同步針對此流的操做的對象
protected Object lock;

 

3、構造函數

protected Writer() { this.lock = this; } protected Writer(Object lock) { if (lock == null) { throw new NullPointerException(); } this.lock = lock; }

 

4、主要方法

一、write()

//寫入單個字符。要寫入的字符包含在給定整數值的16個低位中,16高位被忽略。
    public void write(int c) throws IOException { synchronized (lock) { if (writeBuffer == null){ writeBuffer = new char[WRITE_BUFFER_SIZE]; } writeBuffer[0] = (char) c; write(writeBuffer, 0, 1); } } //將一個字符數組寫入到writerBuffer中
    public void write(char cbuf[]) throws IOException { write(cbuf, 0, cbuf.length); } //試圖將字符數組中從off開始的len個字符寫入輸出流中。儘可能寫入len個字符,但寫入的字節數可能少於len個,也可能爲零。
abstract public void write(char cbuf[], int off, int len) throws IOException; //寫入字符串
public void write(String str) throws IOException { write(str, 0, str.length()); } //試圖將字符串中從off開始的len個字符寫入輸出流中。儘可能寫入len個字符,但寫入的字節數可能少於len個,也可能爲零。
public void write(String str, int off, int len) throws IOException { synchronized (lock) { char cbuf[]; if (len <= WRITE_BUFFER_SIZE) { if (writeBuffer == null) { writeBuffer = new char[WRITE_BUFFER_SIZE]; } cbuf = writeBuffer; } else {    // Don't permanently allocate very large buffers.
                cbuf = new char[len]; } str.getChars(off, (off + len), cbuf, 0); write(cbuf, 0, len); } }

二、append()

/** * 添加字符序列 */
    public Writer append(CharSequence csq) throws IOException { if (csq == null) write("null"); else write(csq.toString()); return this; } /** * 添加字符序列的一部分 */
    public Writer append(CharSequence csq, int start, int end) throws IOException { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } /** * 添加指定字符 */
    public Writer append(char c) throws IOException { write(c); return this; }

三、flush()

/** * 刷新該流的緩衝。 */
    abstract public void flush() throws IOException;

四、close()

/** * 關閉此流,但要先刷新它。 */
    abstract public void close() throws IOException;
相關文章
相關標籤/搜索