一、流的概念
“對語言設計人員來說,創建好的輸入/輸出系統是一項特別困難的任務。”
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?――《Think in Java》
無論是系統、還是語言的設計中IO的設計都是異常復雜的。面臨的最大的挑戰一般是如何覆蓋所有可能的因素,我們不僅僅要考慮文件、控制臺、網絡、內存等不同的種類,而且要處理大量的不同的讀取方式,如:順序讀取、隨機讀取,二進制讀取、字符讀取,按行讀取、按字符讀取……
為了解決輸入/輸出問題,創建了Java IO體系,并且提出了流的概念。
如何理解流的概念?聯想一下現實生活中水流,自來水公司通過管道將水送入千家萬戶,數據相當于現實生活中的水,JAVA中的流相當于現實生活中的管道。在Java中,流代表任何有能力產出數據的數據源對象或者是有能力接受數據的接收端對象,流的作用是數據傳輸,根據數據傳輸特性將流抽象為各種類,方便更直觀的進行數據操作。
ps:當面試問到你對流的理解時,可以拿生活中的自來水來做對比或者從面對對象的角度來討論。
二、Java IO體系
1、基于字節的IO操作
2、基于字符的IO操作
從上圖可以看到,整個Java IO體系都是基于字節流(InputStream/OutputStream) 和字節流(Reader/Writer)作為基類,根據不同的數據載體或功能派生出來的。
3、分類
按照數據單位來分:字節流、字符流
按照方向分:輸入流、輸出流
按照功能來分:節點流、處理流(直接與底層文件資源連接的稱為節點流,對節點流進行包裝從而完成更高級功能的稱為處理流,處理流在構造時須為其指定一個節點流.)
三、擴展
下面我們來看一下JDK1.8的源代碼
3.1、源代碼
3.1.1、InputStream
public abstract int read() throws IOException;//從輸入流中讀取一個字節(值0-255)并返回,子類必須提供此方法的實現,如果到達了流的末尾,則返回-1public int read(byte b[]) throws IOException {return read(b, 0, b.length);}/*** 實際上還是調用read()方法,將讀到的內容放到字節數組中b,返回-1(已讀到流的末尾)或者實際讀取的字節數*/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;}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;}/*** 跳過n個字節,實際上還是調用read()方法,所以read()方法很關鍵,返回實際跳過的字節數*/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;}
上面的源碼和注釋分析了read方法和skip的作用。
3.1.2、OutputStream
public abstract void write(int b) throws IOException;//將指定字節寫入輸出流,只會寫入低8位,高的24位會被忽略,因為一個字節只有8個bit位public void write(byte b[]) throws IOException {write(b, 0, b.length);}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]);}}
3.1.3、Reader
abstract public int read(char cbuf[], int off, int len) throws IOException;//將字符讀入數組,此方法可能會發生阻塞,返回-1或者讀入的字符數public int read(char cbuf[]) throws IOException {return read(cbuf, 0, cbuf.length);}/*** 將字符讀入字符緩沖區*/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;}/*** 讀取單個字符*/public int read() throws IOException {char cb[] = new char[1];if (read(cb, 0, 1) == -1)return -1;elsereturn cb[0];}
3.1.4、Writer
abstract public void write(char cbuf[], int off, int len) throws IOException;//將字符數組的一部分寫入到輸出流/*** 將指定字符寫入到輸出流*/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);}}/*** 將字符數組的所有內容寫入到輸出流*/public void write(char cbuf[]) throws IOException {write(cbuf, 0, cbuf.length);}/*** 將字符串的所有內容寫入到輸出流*/public void write(String str) throws IOException {write(str, 0, str.length());}/*** 將字符串的部分內容寫入到輸出流*/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.2、NIO
對IO的概念有基本的了解之后,我們再來了解一下JAVA中類似的概念NIO。由于本文主要介紹IO的概念,所以對NIO的介紹會很淺顯,后面會有專門的文章來介紹NIO。
NIO( New Input/ Output)一種同步非阻塞的IO模型。同步是指線程不斷輪詢IO事件是否就緒,非阻塞是指線程在等待IO的時候,可以繼續其他任務。同步的核心是Selector,Selector替代了線程本身去輪詢IO事件,避免了阻塞的同時也減少了不必要的線程消耗;非阻塞的核心是通道和緩沖區,當IO事件就緒時,可以通過寫到緩沖區,保證IO的成功,而無需線程阻塞式的等待。
NIO和IO的主要區別
IO | NIO |
面向流 | 面向緩沖 |
阻塞IO | 非阻塞IO |
無 | 選擇器 |
關于通道、緩沖區和選擇器的實現參考https://blog.csdn.net/forezp/article/details/88414741
參考:
- https://blog.csdn.net/qq_36962144/article/details/79815457
- https://www.cnblogs.com/ylspace/p/8128112.html
- https://www.cnblogs.com/fysola/p/6123947.html