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