摘要:一面試題及剖析今日面試題今天壹哥帶各位復習一塊可能會令初學者比較頭疼的內(nèi)容,起碼當時讓我很有些頭疼的內(nèi)容,那就是流。在這里壹哥會從兩部分展開介紹流,即與流。除此之外盡量使用字節(jié)流。關(guān)閉此輸入流并釋放與流相關(guān)聯(lián)的任何系統(tǒng)資源。
今天 壹哥 帶各位復習一塊可能會令初學者比較頭疼的內(nèi)容,起碼當時讓我很有些頭疼的內(nèi)容,那就是I/O流。為啥I/O流會讓很多初學者頭疼呢?其實主要是因為I/O流的分類實在是太多了,一會是輸入流,一會是輸出流,還有字節(jié)流、字符流、文件輸入流,文件輸出流,緩沖流.....亂七八糟一大堆,光是這些英文單詞把人背的腦袋都大了。
正因為如此,面試官就喜歡在這里考察我們的Java基礎(chǔ),常見的I/O流題目如下:
說一下Java中的I/O流有哪些?
你常用哪些I/O流?
輸入流、輸出流的區(qū)別?
......
我們在開發(fā)時,用到I/O流的地方有很多,比如文件的上傳下載,數(shù)據(jù)傳輸、存儲,音視頻編解碼操作等,這些都是很重要的操作,所以面試官就很喜歡考察我們這一塊的基礎(chǔ)是否扎實。如果我們I/O流的基礎(chǔ)不夠扎實,就很難寫出健壯高效的偏底層代碼。
所以接下來 壹哥 會帶各位詳細全面的梳理一下I/O流的內(nèi)容,以后再遇到有關(guān)I/O流的面試題,直接把本博客中的內(nèi)容甩到面試官臉上即可,HIA HIA。
首先 壹哥 來解釋一下 “I/O流” 這個概念,如果我們連 I/O流 是個啥都不知道,也就沒必要繼續(xù)往下看了。在這里 壹哥 會從兩部分展開介紹I/O流,即 “I/O” 與 “流”。
1.1 I/O
I/O中間用 “/”斜杠 分割,其實代表的是兩個內(nèi)容,即 “I” 與 “O”,分別是 “In” 與 “Out” 的縮寫。
In:輸入,代表著能夠接收數(shù)據(jù)的數(shù)據(jù)源對象;
Out:輸出,代表著能夠產(chǎn)出數(shù)據(jù)的數(shù)據(jù)源對象。
1.2 流
接下來咱們再來看看什么是 “流”!請先在腦海里想一下,“流”是一種什么樣的形態(tài)?其實Java中各個API的命名都是很形象的,絕對都做到了見名知意。
這里 “流” 就是一個很形象的概念!當我們的代碼程序需要 讀入數(shù)據(jù) 的時候,可以開啟一個連通 數(shù)據(jù)源 的流(輸入流),這個數(shù)據(jù)源可以是文件、內(nèi)存、數(shù)據(jù)庫,或是網(wǎng)絡連接。同樣的,當我們的代碼程序需要 輸出數(shù)據(jù) 的時候,可以開啟一個連通 目的地 的流(輸出流),這個目的地一般是指我們的代碼程序。這時候你可以想象一下,我們的數(shù)據(jù)好像就在數(shù)據(jù)源與目的地之間 “流動” 起來了一樣。
其實 流(stream)這個概念,一開始源于UNIX中的管道(pipe)概念。在UNIX中,管道是一條不間斷的字節(jié)流,用來實現(xiàn)程序或進程間的通信,或讀寫外圍設備、外部文件等。
最后 壹哥 再給各位提取一下流的概念:
流是一組有順序的,有起點和終點的字節(jié)集合,是對數(shù)據(jù)傳輸?shù)目偡Q或抽象。即數(shù)據(jù)在兩設備間的傳輸稱為流,流的本質(zhì)是數(shù)據(jù)傳輸,根據(jù)數(shù)據(jù)傳輸特性將流抽象為各種類,方便更直觀的進行數(shù)據(jù)操作。
而且從上面我對I/O流的描述中,我們也可以抽取出I/O流的核心作用,如下:
I/O流可以在數(shù)據(jù)源和目的地之間搭建一個傳輸通道,用于處理設備與代碼程序之間的數(shù)據(jù)傳輸,設備是指硬盤、內(nèi)存、數(shù)據(jù)庫、鍵盤錄入、網(wǎng)絡等。
一言以蔽之,I/O流屏蔽了實際的I/O設備中處理數(shù)據(jù)的各種細節(jié),我們不必關(guān)心其內(nèi)部具體的流動過程,只需知道I/O流可以用來處理設備之間的數(shù)據(jù)傳輸即可。
如果只看上面關(guān)于I/O流的概念,感覺也并沒有什么難度,但是對I/O流的學習,最難的是在于其分類實在是太多。I/O流中有著不同的劃分維度,如果我們根據(jù)這些不同的標準來分類的話,可以分類如下:
接下來 壹哥 再分別對這3種分類進行詳解一下。
3.1 輸入流與輸出流
從I/O流的流動方向上,我們知道I/O流其實可以分為輸入流與輸出流,但是不少初學者總是分不清輸入流與輸出流,甚至會把兩者搞反。所以接下來 壹哥 就再明確一下輸入流與輸出流的區(qū)別,我們來看下圖:
在上圖中,我們以家中自來水的進水與出水來形象的比喻輸入流與輸出流。
自來水公司相當于是數(shù)據(jù)源,我們家中的房子就相當于是目的地。自來水公司的水進入到我們家里,這就是自來水的輸入;我們家中產(chǎn)生的污水,要排到污水處理廠,這就是自來水的輸出。
在這個自來水供水、排水的過程中,我們可以想一下,輸入、輸出是不是一個相對的概念呢?那么相對于哪個角色呢?沒錯!輸入、輸出都是相對于我們的房子來說的,進入到房子叫做輸入,流出房子叫做輸出。
壹哥 再把上圖中的各角色明確一下:
數(shù)據(jù)源文件:就是上圖中自來水公司的水池,用于提供自來水(數(shù)據(jù));
輸入流:從自來水公司進到房子里的管道流,攜帶著具體的數(shù)據(jù)到家里來;
目的地:就是上圖中的房子,也就是我們項目的代碼程序,或者說是內(nèi)存;
輸出流:從家中流出到污水廠的管道流,攜帶著具體的數(shù)據(jù)到污水廠;
數(shù)據(jù)目標文件:最后的污水廠,其實也就是用于持久化存儲污水(數(shù)據(jù))的地方,其實也是一種數(shù)據(jù)源。
所以,I/O流中的輸入流與輸出流,入與出都是相對于內(nèi)存而言的。從某個數(shù)據(jù)源讀取數(shù)據(jù)到內(nèi)存中,被稱為輸入流;從內(nèi)存中把數(shù)據(jù)持久化保存到其他設備上,則被稱為輸出流。簡單一句話,流向內(nèi)存是輸入流,流出內(nèi)存的輸出流。我們再來看下圖:
另外要注意,我們可以從輸入流中讀取信息,但不能對它寫;可以對輸出流進行寫操作,但不能從中讀。所以輸入也叫做讀取數(shù)據(jù),輸出也叫做作寫出數(shù)據(jù)!
至此,你應該不會再把輸入流和輸出流搞反了吧,是不是應該給 壹哥 點個贊,hiahia......
3.2 字節(jié)流與字符流
上面 壹哥 說了,按照數(shù)據(jù)流的數(shù)據(jù)單位不同,I/O流可以分為字節(jié)流與字符流,兩者的區(qū)別如下:
字節(jié)流:字節(jié)流以字節(jié)(8bit)為單位,一次讀入或?qū)懗?位二進制數(shù)據(jù);字節(jié)流能處理所有類型的數(shù)據(jù)(如圖片、音頻、視頻等);
字符流:字符流以字符為單位,根據(jù)碼表映射字符,一次可能讀多個字節(jié),一次讀入或?qū)懗?6位二進制數(shù)據(jù);字符流只能處理字符類型的數(shù)據(jù)。
這是因為在Java中,一個字節(jié)的空間是1個Byte,即8位;而一個字符的空間是2個Byte,即16位。另外Java的字節(jié)是有符號類型,字符是無符號類型!
另外我們要知道,在計算機里,一切文件數(shù)據(jù)(文本、圖片、視頻等)在存儲時,都是以二進制的形式保存的,即都是一個一個的字節(jié),在傳輸時也一樣如此。所以,字節(jié)流可以傳輸任意類型的文件數(shù)據(jù)。在操作流的時候,我們要時刻明確,無論使用什么樣的流對象,底層傳輸?shù)氖冀K為二進制數(shù)據(jù)。
但是因為數(shù)據(jù)編碼的不同,為了對字符進行高效的操作,就有了字符流。字符流的本質(zhì)其實也是基于字節(jié)流,在進行讀取時去查了指定的碼表,而字節(jié)流直接讀取數(shù)據(jù)會有亂碼的問題(讀中文會亂碼)。
所以字節(jié)流和字符流的原理其實也是相同的,只不過處理的數(shù)據(jù)單位大小不同而已。一般在Java關(guān)于I/O流的API里,后綴是Stream的是字節(jié)流,而后綴是Reader和Writer的是字符流。
你可能會問,我們開發(fā)時,到底是該選擇字節(jié)流還是字符流呢?
對于字節(jié)流和字符流,如果我們只是要處理純文本數(shù)據(jù),可以優(yōu)先考慮字符流。 除此之外盡量使用字節(jié)流。
3.3 節(jié)點流與處理流
另外如果從I/O流的功能角度來看,I/O流可以分為節(jié)點流和處理流,兩者區(qū)別如下:
節(jié)點流:直接與數(shù)據(jù)源相連,讀入或?qū)懗觥?/strong>
但是如果我們直接使用節(jié)點流進行操作,讀寫并不方便,所以為了更快的讀寫文件,才有了處理流。
處理流:一般會與節(jié)點流一起使用,在節(jié)點流的基礎(chǔ)上,再套接一層,套接在節(jié)點流上的就是處理流。
在Java中,關(guān)于I/O流的API,共有四大類,分別如下:
InputStream---字節(jié)輸入流;
OutputStream---字節(jié)輸出流;
Reader-----字符輸入流;
Writer------字符輸出流。
即Java中字節(jié)流的抽象基類有如下2個:
InputStream,OutputStream
InputStream與OutputStream類關(guān)系如下圖所示:
字符流的抽象基類有如下2個:
Reader,Writer
Reader與Writer類關(guān)系如下圖所示:
我們可以用下圖概括展示:
由這四個基類派生出來的子類名稱,都是以其父類名作為子類名的后綴,如InputStream的子類FileInputStream,Reader的子類FileReader。
4.1 InputStream字節(jié)輸入流
InputStream的常用方法:
int available() 從下一次調(diào)用此輸入流的方法返回可從該輸入流讀?。ɑ蛱^)的字節(jié)數(shù),而不會阻塞。void close() 關(guān)閉此輸入流并釋放與流相關(guān)聯(lián)的任何系統(tǒng)資源。 void mark(int readlimit) 標記此輸入流中的當前位置。 boolean markSupported() 測試此輸入流是否支持 mark和 reset方法。 abstract int read() 從輸入流讀取數(shù)據(jù)的下一個字節(jié)。 int read(byte[] b) 從輸入流中讀取一些字節(jié)數(shù),并將它們存儲到緩沖器陣列 b。 int read(byte[] b, int off, int len) 從輸入流讀取最多 len個字節(jié)的數(shù)據(jù)到字節(jié)數(shù)組。byte[] readAllBytes() 從輸入流讀取所有剩余字節(jié)。 int readNBytes(byte[] b, int off, int len) 將所請求的字節(jié)數(shù)從輸入流讀入給定的字節(jié)數(shù)組。 void reset() 將此流重新定位到最后在此輸入流上調(diào)用 mark方法時的位置。 long skip(long n) 跳過并丟棄來自此輸入流的 n字節(jié)的數(shù)據(jù)。 long transferTo(OutputStream out) 從該輸入流中讀取所有字節(jié),并按讀取的順序?qū)⒆止?jié)寫入給定的輸出流。
4.2 OutputStream字節(jié)輸出流
OutputStream字節(jié)輸出流的方法:
void close() 關(guān)閉此輸出流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。 void flush() 刷新此輸出流并強制任何緩沖的輸出字節(jié)被寫出。 void write(byte[] b) 將 b.length字節(jié)從指定的字節(jié)數(shù)組寫入此輸出流。 void write(byte[] b, int off, int len) 從指定的字節(jié)數(shù)組寫入 len字節(jié),從偏移量 off開始輸出到此輸出流。 abstract void write(int b) 將指定的字節(jié)寫入此輸出流。
4.3 Reader字符輸入流
Reader主要方法如下:
abstract void close() 關(guān)閉流并釋放與之相關(guān)聯(lián)的任何系統(tǒng)資源。 void mark(int readAheadLimit) 標記流中的當前位置。 boolean markSupported() 告訴這個流是否支持mark()操作。 int read() 讀一個字符 int read(char[] cbuf) 將字符讀入數(shù)組。abstract int read(char[] cbuf, int off, int len) 將字符讀入數(shù)組的一部分。 int read(CharBuffer target) 嘗試將字符讀入指定的字符緩沖區(qū)。boolean ready() 告訴這個流是否準備好被讀取。 void reset() 重置流。 long skip(long n) 跳過字符
4.4 Writer字符輸出流
Writer的主要方法如下:
Writer append(char c) 將指定的字符附加到此writerWriter append(CharSequence csq) 將指定的字符序列附加到此writerWriter append(CharSequence csq, int start, int end) 將指定字符序列的子序列附加到此writerabstract void close() 關(guān)閉流,先刷新abstract void flush() 刷新流 void write(char[] cbuf) 寫入一個字符數(shù)組。 abstract void write(char[] cbuf, int off, int len) 寫入字符數(shù)組的一部分 void write(int c) 寫一個字符 void write(String str) 寫一個字符串 void write(String str, int off, int len) 寫一個字符串的一部分
4.5 完整API圖
以上這些java.io包中的API,我給大家繪制了下圖:
從上圖中,我們可以看到,有各種各樣的I/O流相關(guān)的API類,我們可以簡單歸納并做如下簡介:
- 文件操作流:
- FileInputStream(字節(jié)輸入流);
- FileOutputStream(字節(jié)輸出流);
- FileReader(字符輸入流);
- FileWriter(字符輸出流)
- 管道操作流:
- PipedInputStream(字節(jié)輸入流);
- PipedOutStream(字節(jié)輸出流);
- PipedReader(字符輸入流);
- PipedWriter(字符輸出流)
注意:
PipedInputStream的一個實例要和PipedOutputStream的一個實例共同使用,共同完成管道的讀取寫入操作,主要用于線程操作。
- 字節(jié)/字符數(shù)組操作流:
- ByteArrayInputStream(字節(jié)數(shù)組輸入流);
- ByteArrayOutputStream(字節(jié)數(shù)組輸出流);
- CharArrayReader(字符數(shù)組輸入流);
- CharArrayWriter(字符數(shù)組輸出流)
- Buffered緩沖流:
- BufferedInputStream(字節(jié)緩沖區(qū)輸入流);
- BufferedOutputStream(字節(jié)緩沖區(qū)輸出流);
- BufferedReader(字符緩沖區(qū)輸入流);
- BufferedWriter(字符緩沖區(qū)輸出流)
注意:
這是帶緩沖區(qū)的處理流,緩沖流的底層從具體設備上獲取數(shù)據(jù),并將數(shù)據(jù)存儲到緩沖區(qū)的數(shù)組內(nèi),通過緩沖區(qū)的read()方法從緩沖區(qū)獲取具體的字符數(shù)據(jù),這樣就避免了每次都和硬盤打交道,提高了數(shù)據(jù)訪問效率。
- 轉(zhuǎn)化流:
- InputStreamReader
- OutputStreamWriter
注意:
轉(zhuǎn)換流,從字面意思可以看出它是字節(jié)流與字符流之間的橋梁,可以將字節(jié)轉(zhuǎn)為字符,或者將字符轉(zhuǎn)為字節(jié)。
- 數(shù)據(jù)流:
- DataInputStream
- DataOutputStream
注意:
數(shù)據(jù)流可以解決我們輸出數(shù)據(jù)類型的困難,數(shù)據(jù)流可以直接輸出float類型或long類型,提高了數(shù)據(jù)讀寫的效率。
- 打印流:
- PrintStream
- PrintWriter
注意:
一般是打印到控制臺,可以進行控制打印的地方。
- 對象流:
- ObjectInputStream
- ObjectOutputStream
注意:
ObjectOutputStream可以將Java對象的原始數(shù)據(jù)類型寫出到文件,實現(xiàn)對象的持久存儲;
ObjectInputStream反序列化流,將之前使用ObjectOutputStream序列化的原始數(shù)據(jù)恢復為對象。
- 序列化流:
- SequenceInputStream
回到我們的面試題上面來,Java中的I/O流有哪些,其實用下圖即可回答。
我們知道,Java中的I/O流有很多,但是開發(fā)時并不是每一個都經(jīng)常用到的,所以 壹哥 用下圖給各位展示了常用的I/O流,下圖中凡是帶有中文解釋的I/O流,基本就是我們開發(fā)時最常用的。
最后我們把IO流再簡單總結(jié)一下。
針對讀寫對象的不同,
字節(jié)流可以采用帶緩沖區(qū)的BufferedInputStream和BufferedOutputStream;
字符流可以采用帶緩沖區(qū)的BufferedReader和BufferedWriter。
字節(jié)流:
InputStream;
OutputStream
字符流:
Reader;
Writer
Java中其他各種流都是由它們派生出來的。
FileReader
FileWriter
DataInputStream
DataOutputStream
BufferedReader
BufferedWriter
BufferedInputStream
BufferedOutputStream
FileInputStream/FileOutputStream:需要逐個字節(jié)處理原始二進制流的時候使用,效率低下。
FileReader/FileWriter:需要組個字符處理的時候使用。
StringReader/StringWriter:需要處理字符串的時候,可以將字符串保存為字符數(shù)組。
PrintStream/PrintWriter:用來包裝FileOutputStream 對象,方便直接將String字符串寫入文件。
Scanner:用來包裝System.in流,很方便地將輸入的String字符串轉(zhuǎn)換成需要的數(shù)據(jù)類型。
InputStreamReader/OutputStreamReader,:字節(jié)和字符的轉(zhuǎn)換橋梁,在網(wǎng)絡通信或者處理鍵盤輸入的時候用。
BufferedReader/BufferedWriter, BufferedInputStream/BufferedOutputStream:緩沖流用來包裝字節(jié)流后者字符流,提升IO性能,BufferedReader還可以方便地讀取一行,簡化編程。
SequenceInputStream(InputStream s1, InputStream s2):序列流,合并流對象時使用。
ObjectInputStream、ObjectOutputStream:方法用于序列化對象并將它們寫入一個流,另一個方法用于讀取流并反序列化對象。
ByteArrayInputStream、ByteArrayOutputStream:用于操作字節(jié)數(shù)組。
DataInputStream、DataOutputStream:操作基本數(shù)據(jù)類型和字符串。
我們現(xiàn)在已經(jīng)知道了這么多的 I/O流 分類,那在開發(fā)時該選擇使用哪種呢?什么時候用輸出流?什么時候用字節(jié)流?我們可以根據(jù)下面三步選擇適合自己的流:
- 首先到底該選擇輸入流還是輸出流,這就要根據(jù)自己實際需求的情況來決定,如果想從程序內(nèi)存中輸出數(shù)據(jù)到別的設備中,那么就選擇輸出流,反之就選輸入流;
- 然后我們再考慮數(shù)據(jù)輸出時,每次是要傳遞一個字節(jié)還是兩個字節(jié),每次傳輸一個字節(jié)就選字節(jié)流,如果存在中文,那肯定就要選字符流了;
- 通過前面兩步,我們就可以選出一個合適的節(jié)點流了,比如字節(jié)輸入流 InputStream,如果要在此基礎(chǔ)上增強功能,那么就在處理流中再選擇一個合適的即可。
至此,壹哥 就把I/O流中的核心內(nèi)容帶大家都復習了一下,當然還有挺多細節(jié)沒有總結(jié)到位,畢竟我這里是面試題梳理總結(jié),不是專門的I/O流教程,請各位再結(jié)合之前學習I/O流時的內(nèi)容,形成自己的知識脈絡。如果你有什么想法,可以在評論區(qū)留言討論哦。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/124493.html
摘要:我的是忙碌的一年,從年初備戰(zhàn)實習春招,年三十都在死磕源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習。因為我心理很清楚,我的目標是阿里。所以在收到阿里之后的那晚,我重新規(guī)劃了接下來的學習計劃,將我的短期目標更新成拿下阿里轉(zhuǎn)正。 我的2017是忙碌的一年,從年初備戰(zhàn)實習春招,年三十都在死磕JDK源碼,三月份經(jīng)歷了阿里五次面試,四月順利收到實習offer。然后五月懷著忐忑的心情開始了螞蟻金...
摘要:但它融合了和的功能。支持對隨機訪問文件的讀取和寫入。的概述和作為集合的使用了解的概述類表示了一個持久的屬性集??杀4嬖诹髦谢驈牧髦屑虞d。屬性列表中每個鍵及其對應值都是一個字符串。 1_序列流(了解) 1.什么是序列流 序列流可以把多個字節(jié)輸入流整合成一個, 從序列流中讀取數(shù)據(jù)時, 將從被整合的第一個流開始讀, 讀完一個之后繼續(xù)讀第二個, 以此類推. 2.使用方式 整合兩個: S...
摘要:字符流字符流是什么字符流是可以直接讀寫字符的流字符流讀取字符就要先讀取到字節(jié)數(shù)據(jù)然后轉(zhuǎn)為字符如果要寫出字符需要把字符轉(zhuǎn)為字節(jié)再寫出類的方法可以按照字符大小讀取通過項目默認的碼表一次讀取一個字符賦值給將讀到的字符強轉(zhuǎn)后打印字符流類的方法可以 1_字符流FileReader 1.字符流是什么 字符流是可以直接讀寫字符的IO流 字符流讀取字符, 就要先讀取到字節(jié)數(shù)據(jù), 然后轉(zhuǎn)為字符. ...
摘要:目錄介紹問題匯總具體問題好消息博客筆記大匯總年月到至今,包括基礎(chǔ)及深入知識點,技術(shù)博客,學習筆記等等,還包括平時開發(fā)中遇到的匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續(xù)完善開源的文件是格式的同時也開源了生活博客,從年 目錄介紹 00.Java問題匯總 01.具體問題 好消息 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點,Android技...
摘要:是字節(jié)流通向字符流的橋梁它使用指定的讀取字節(jié)并將其解碼為字符。解碼把看不懂的變成能看懂的繼承自父類的共性成員方法讀取單個字符并返回。一次讀取多個字符將字符讀入數(shù)組。關(guān)閉該流并釋放與之關(guān)聯(lián)的所有資源。構(gòu)造方法創(chuàng)建一個使用默認字符集的。 package com.itheima.demo03.ReverseStream; import java.io.FileInputStream;impo...
閱讀 3200·2021-11-23 09:51
閱讀 2045·2021-09-09 09:32
閱讀 1136·2019-08-30 15:53
閱讀 3023·2019-08-30 11:19
閱讀 2542·2019-08-29 14:15
閱讀 1499·2019-08-29 13:52
閱讀 599·2019-08-29 12:46
閱讀 2884·2019-08-26 12:18