Java IO (二)抽象基類InputStream、OutputStream、Reader、Writer
IO流的抽象基類分位元組輸入流(InputStream)、位元組輸出流(OutputStream)、字元輸入流(Reader)、字元輸出流(Writer)。它們是IO流中的抽象父類,是Java IO操作的基礎。流都實現了Closeable介面,即都需要關閉,輸出流則額外實現了一個Flushable方法,主要是通知立刻將資料刷入指定輸出位置。
2.1 InputStream
java.io.InputStream
是一個抽象類,是位元組輸入流的所有類的超類,除了繼承自Object的屬性和方法,InputStream包含9個方法:
-
abstract int read()
方法是一個虛擬方法,從輸入流中讀取下一個位元組,其子類必須實現這個方法。 -
int read(byte[] b,int off,int len)
方法將流讀入byte[]陣列中第off個位置開始,讀取len個位元組。其實現有呼叫read()
方法進行讀取,讀取前判斷引數是否合理,若讀取到-1則退出(流以-1判斷是否結束),並返回已經讀取的資料個數,若讀取的個數為0則返回-1。public int read(byte b[], int off, int len) throws IOException { if (b == null) {
-
int read(byte[])
方法用處是將流的輸入讀入byte[]陣列中,其實際上呼叫的是read(byte[],int,int)
。 -
long skip(long)
,跳過指定個位元組不讀。其具體實現是:建立了一個跳過的緩衝資料,將讀到這裡面去,跳過的位元組有長度限制,為2048個。通過迴圈排除IO異常的感染,知道讀完或者讀滿要跳過的,然後返回實際跳過的量。public long skip(long n) throws IOException { long remaining = n; int nr; if (n <= 0) { return 0; } int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining); byte[] skipBuffer = new byte[size]; while (remaining > 0) { nr = read(skipBuffer, 0, (int)Math.min(size, remaining)); if (nr < 0) { break; } remaining -= nr; } return n - remaining; }
-
int available()
返回估測的可讀的數量,在InputStream中是直接return 0
。這個方法對於所有流的實現來說不都是準確的,所以慎用。這個方法需要被子類覆寫。單次讀或跳過很多位元組不會阻塞,但是可能會讀取或跳過少量位元組。 -
void close()
,關閉流,但是在InputStream中沒有具體實現,會釋放作業系統資源。 -
synchronized void mark(int readlimit)
標記讀取位置,如果流實現支援,就可以重置位置,下次讀取則一模一樣。InputStream中什麼都沒做。使用之前要通過markSupported方法判斷一下是否支援。 -
synchronized void reset()
reset到mark方法最後一次呼叫的位置,InputStream中並沒有實現mark方法,所以reset方法直接丟擲異常。 -
boolean markSupported()
判斷當前流是否支援標記流。
2.2 OutputStream
java.io.OutputStream
此抽象類是表示輸出位元組流的所有類的超累,輸出流接受輸出位元組並將這些位元組傳送到某個接收器,除了繼承自Object的屬性和方法,OutputStream包含5個方法:
-
abstract void write(int b)
,抽象方法,通常是寫入一個位元組byte,低8位將寫入,高24位會被忽略。將指定位元組寫入輸出位置,子類必須實現這個方法。 -
void write(byte b[], int off, int len)
方法,將陣列b的起始位置off開始,寫入len長度的位元組。其實現包含邊界判定,最後通過迴圈呼叫write
方法實現。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]); } }
-
void write(byte b[])
通過呼叫方法2:write(b,0,b.length)實現 -
void flush()
重新整理此輸出流並強制攜出所有緩衝的輸出位元組。flush的常規協定是:如果此輸出流的實現已經緩衝了以前寫入的任何位元組,則呼叫flush方法指示應將這些位元組立即寫入它們的預期目標。但是如果此流的預期目標是由基礎作業系統提供的一個抽象(如一個檔案),則flush只能保證將以前寫入到流的位元組傳遞給作業系統進行寫入,不保證能立即將這些位元組寫入到物理裝置(如磁碟驅動器)。在OutputStream中該方法不執行任何操作。 -
void close()
關閉輸出流並釋放與此流有關的所有系統資源。close的常規協定是:該方法將關閉輸出流,關閉的流不能執行輸出操作,也不能重新發開。OutputStream的close方法不執行人格操作。
2.3 Reader
java.io.Reader
實現了介面Closeable和Readable,其是用於讀取字元流的抽象類。
相比於位元組輸入流InputStream,Reader中不同的有以下幾點:
-
lock物件 lock是Object型別的物件,用於同步針對此流的操作的物件。為了提高效率,字元流可以使用自身以外的物件來保護重要部分,因此,子類應使用此欄位中的物件而不是this或者同步的方法。構造方法中Reader()所做的是建立一個新的字元流,其重要部分將同步其自身的reader;而Reader(Object lock)則是建立一個新的字元流reader,其重要部分將同步給定的物件。
-
Reader的三個read方法中,
read()
方法和read(char cbuf[])
都是使用read(char cbuf[], int off, int len)
實現的,而子類必須實現read(char cbuf[], int off, int len)
方法,注意:用於支援高效單字元輸入的子類必須重寫read()
方法/*用於支援高效單字元輸入的子類必須重寫該方法*/ public int read() throws IOException { char cb[] = new char[1]; if (read(cb, 0, 1) == -1) return -1; else return cb[0]; } public int read(char cbuf[]) throws IOException { return read(cbuf, 0, cbuf.length); } /*子類必須實現該方法*/ abstract public int read(char cbuf[], int off, int len) throws IOException;
-
read(CharBuffer target)
方法:該方法試圖將字元讀入指定的字元緩衝區。緩衝區可照鴛鴦用作字元的儲存庫:所做的唯一改變時put操作的結果。不對緩衝區執行翻轉或重繞操作。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; }
-
*ready()方法** 該方法在Reader中是直接返回flase的,其用來判斷是否準備讀取此流。如果保證下一個read()不阻塞輸入,則返回True,否則返回false。注意,返回false並不保證下一次讀取。
-
skip()方法的長度限制是8192
2.4 Writer
java.io.Writer
實現了
Writer相比於位元組輸出流OutputStream有更多的不同。
-
lock物件
-
write方法 Writer提供了5個write方法,其中子類需要實現的是
void write(char cbuf[], int off, int len)
。/*該方法使用write(char cbuf[], int off, int len)方法實現*/ 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); } } /*該方法使用write(char cbuf[], int off, int len)方法實現*/ public void write(char cbuf[]) throws IOException { write(cbuf, 0, cbuf.length); } /*子類必須實現該方法*/ abstract public void write(char cbuf[], int off, int len) throws IOException; /*該方法使用write(String str, int off, int len)方法實現*/ public void write(String str) throws IOException { write(str, 0, str.length()); } /*該方法使用write(char cbuf[], int off, int 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); } }
3.append方法都來自於其實現的Appendable介面,用於將指定字元或字元序列新增到此writer。
-
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; }