您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Java IO流常見面試題有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Java IO流常見面試題有哪些”吧!
在這一小節(jié),我會試著給出Java IO(java.io)包下所有類的概述。更具體地說,我會根據(jù)類的用途對類進行分組。這個分組將會使你在未來的工作中,進行類的用途判定時,或者是為某個特定用途選擇類時變得更加容易。
輸入和輸出
術語“輸入”和“輸出”有時候會有一點讓人疑惑。一個應用程序的輸入往往是另外一個應用程序的輸出 那么OutputStream流到底是一個輸出到目的地的流呢,還是一個產(chǎn)生輸出的流?InputStream流到底會不會輸出它的數(shù)據(jù)給讀取數(shù)據(jù)的程序呢?就我個人而言,在第一天學習Java IO的時候我就感覺到了一絲疑惑。 為了消除這個疑惑,我試著給輸入和輸出起一些不一樣的別名,讓它們從概念上與數(shù)據(jù)的來源和數(shù)據(jù)的流向相聯(lián)系。
Java的IO包主要關注的是從原始數(shù)據(jù)源的讀取以及輸出原始數(shù)據(jù)到目標媒介。以下是最典型的數(shù)據(jù)源和目標媒介:
文件 管道 網(wǎng)絡連接 內(nèi)存緩存 System.in, System.out, System.error(注:Java標準輸入、輸出、錯誤輸出)
流
在Java IO中,流是一個核心的概念。流從概念上來說是一個連續(xù)的數(shù)據(jù)流。你既可以從流中讀取數(shù)據(jù),也可以往流中寫數(shù)據(jù)。流與數(shù)據(jù)源或者數(shù)據(jù)流向的媒介相關聯(lián)。在Java IO中流既可以是字節(jié)流(以字節(jié)為單位進行讀寫),也可以是字符流(以字符為單位進行讀寫)。
類InputStream, OutputStream, Reader 和Writer
一個程序需要InputStream或者Reader從數(shù)據(jù)源讀取數(shù)據(jù),需要OutputStream或者Writer將數(shù)據(jù)寫入到目標媒介中。
InputStream和Reader與數(shù)據(jù)源相關聯(lián),OutputStream和writer與目標媒介相關聯(lián)。
Java IO的用途和特征
Java IO中包含了許多InputStream、OutputStream、Reader、Writer的子類。這樣設計的原因是讓每一個類都負責不同的功能。這也就是為什么IO包中有這么多不同的類的緣故。各類用途匯總如下:
文件訪問 網(wǎng)絡訪問 內(nèi)存緩存訪問 線程內(nèi)部通信(管道) 緩沖 過濾 解析 讀寫文本 (Readers / Writers) 讀寫基本類型數(shù)據(jù) (long, int etc.) 讀寫對象
當通讀過Java IO類的源代碼之后,我們很容易就能了解這些用途。這些用途或多或少讓我們更加容易地理解,不同的類用于針對不同業(yè)務場景。
Java IO類概述表
已經(jīng)討論了數(shù)據(jù)源、目標媒介、輸入、輸出和各類不同用途的Java IO類,接下來是一張通過輸入、輸出、基于字節(jié)或者字符、以及其他比如緩沖、解析之類的特定用途劃分的大部分Java IO類的表格。
Java IO流是既可以從中讀取,也可以寫入到其中的數(shù)據(jù)流。正如這個系列教程之前提到過的,流通常會與數(shù)據(jù)源、數(shù)據(jù)流向目的地相關聯(lián),比如文件、網(wǎng)絡等等。
流和數(shù)組不一樣,不能通過索引讀寫數(shù)據(jù)。在流中,你也不能像數(shù)組那樣前后移動讀取數(shù)據(jù),除非使用RandomAccessFile 處理文件。流僅僅只是一個連續(xù)的數(shù)據(jù)流。
某些類似PushbackInputStream 流的實現(xiàn)允許你將數(shù)據(jù)重新推回到流中,以便重新讀取。然而你只能把有限的數(shù)據(jù)推回流中,并且你不能像操作數(shù)組那樣隨意讀取數(shù)據(jù)。流中的數(shù)據(jù)只能夠順序訪問。
Java IO流通常是基于字節(jié)或者基于字符的。字節(jié)流通常以“stream”命名,比如InputStream和OutputStream。除了DataInputStream 和DataOutputStream 還能夠讀寫int, long, float和double類型的值以外,其他流在一個操作時間內(nèi)只能讀取或者寫入一個原始字節(jié)。
字符流通常以“Reader”或者“Writer”命名。字符流能夠讀寫字符(比如Latin1或者Unicode字符)。可以瀏覽Java Readers and Writers獲取更多關于字符流輸入輸出的信息。
InputStream
java.io.InputStream類是所有Java IO輸入流的基類。如果你正在開發(fā)一個從流中讀取數(shù)據(jù)的組件,請嘗試用InputStream替代任何它的子類(比如FileInputStream)進行開發(fā)。這么做能夠讓你的代碼兼容任何類型而非某種確定類型的輸入流。
組合流
你可以將流整合起來以便實現(xiàn)更高級的輸入和輸出操作。比如,一次讀取一個字節(jié)是很慢的,所以可以從磁盤中一次讀取一大塊數(shù)據(jù),然后從讀到的數(shù)據(jù)塊中獲取字節(jié)。為了實現(xiàn)緩沖,可以把InputStream包裝到BufferedInputStream中。
代碼示例
InputStream input = new BufferedInputStream(new FileInputStream(“c:\data\input-file.txt”));
緩沖同樣可以應用到OutputStream中。你可以實現(xiàn)將大塊數(shù)據(jù)批量地寫入到磁盤(或者相應的流)中,這個功能由BufferedOutputStream實現(xiàn)。
緩沖只是通過流整合實現(xiàn)的其中一個效果。你可以把InputStream包裝到PushbackInputStream中,之后可以將讀取過的數(shù)據(jù)推回到流中重新讀取,在解析過程中有時候這樣做很方便?;蛘?,你可以將兩個InputStream整合成一個SequenceInputStream。
將不同的流整合到一個鏈中,可以實現(xiàn)更多種高級操作。通過編寫包裝了標準流的類,可以實現(xiàn)你想要的效果和過濾器。
在Java應用程序中,文件是一種常用的數(shù)據(jù)源或者存儲數(shù)據(jù)的媒介。所以這一小節(jié)將會對Java中文件的使用做一個簡短的概述。這篇文章不會對每一個技術細節(jié)都做出解釋,而是會針對文件存取的方法提供給你一些必要的知識點。在之后的文章中,將會更加詳細地描述這些方法或者類,包括方法示例等等。
通過Java IO讀文件
如果你需要在不同端之間讀取文件,你可以根據(jù)該文件是二進制文件還是文本文件來選擇使用FileInputStream或者FileReader。 這兩個類允許你從文件開始到文件末尾一次讀取一個字節(jié)或者字符,或者將讀取到的字節(jié)寫入到字節(jié)數(shù)組或者字符數(shù)組。你不必一次性讀取整個文件,相反你可以按順序地讀取文件中的字節(jié)和字符。
如果你需要跳躍式地讀取文件其中的某些部分,可以使用RandomAccessFile。
通過Java IO寫文件
如果你需要在不同端之間進行文件的寫入,你可以根據(jù)你要寫入的數(shù)據(jù)是二進制型數(shù)據(jù)還是字符型數(shù)據(jù)選用FileOutputStream或者FileWriter。 你可以一次寫入一個字節(jié)或者字符到文件中,也可以直接寫入一個字節(jié)數(shù)組或者字符數(shù)據(jù)。數(shù)據(jù)按照寫入的順序存儲在文件當中。
通過Java IO隨機存取文件
正如我所提到的,你可以通過RandomAccessFile對文件進行隨機存取。
隨機存取并不意味著你可以在真正隨機的位置進行讀寫操作,它只是意味著你可以跳過文件中某些部分進行操作,并且支持同時讀寫,不要求特定的存取順序。 這使得RandomAccessFile可以覆蓋一個文件的某些部分、或者追加內(nèi)容到它的末尾、或者刪除它的某些內(nèi)容,當然它也可以從文件的任何位置開始讀取文件。
下面是具體例子:
@Test //文件流范例,打開一個文件的輸入流,讀取到字節(jié)數(shù)組,再寫入另一個文件的輸出流 public void test1() { try { FileInputStream fileInputStream = new FileInputStream(new File("a.txt")); FileOutputStream fileOutputStream = new FileOutputStream(new File("b.txt")); byte []buffer = new byte[128]; while (fileInputStream.read(buffer) != -1) { fileOutputStream.write(buffer); } //隨機讀寫,通過mode參數(shù)來決定讀或者寫 RandomAccessFile randomAccessFile = new RandomAccessFile(new File("c.txt"), "rw"); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
Java IO的Reader和Writer除了基于字符之外,其他方面都與InputStream和OutputStream非常類似。他們被用于讀寫文本。InputStream和OutputStream是基于字節(jié)的,還記得嗎?
Reader
Reader類是Java IO中所有Reader的基類。子類包括BufferedReader,PushbackReader,InputStreamReader,StringReader和其他Reader。
Writer
Writer類是Java IO中所有Writer的基類。子類包括BufferedWriter和PrintWriter等等。
這是一個簡單的Java IO Reader的例子:
Reader reader = new FileReader("c:\\data\\myfile.txt"); int data = reader.read(); while(data != -1){ char dataChar = (char) data; data = reader.read(); }
你通常會使用Reader的子類,而不會直接使用Reader。Reader的子類包括InputStreamReader,CharArrayReader,F(xiàn)ileReader等等。可以查看Java IO概述瀏覽完整的Reader表格。
整合Reader與InputStream
一個Reader可以和一個InputStream相結合。如果你有一個InputStream輸入流,并且想從其中讀取字符,可以把這個InputStream包裝到InputStreamReader中。把InputStream傳遞到InputStreamReader的構造函數(shù)中:
Reader reader = new InputStreamReader(inputStream);
在構造函數(shù)中可以指定解碼方式。
Writer
Writer類是Java IO中所有Writer的基類。子類包括BufferedWriter和PrintWriter等等。這是一個Java IO Writer的例子:
Writer writer = new FileWriter("c:\\data\\file-output.txt"); writer.write("Hello World Writer"); writer.close();
同樣,你最好使用Writer的子類,不需要直接使用Writer,因為子類的實現(xiàn)更加明確,更能表現(xiàn)你的意圖。常用子類包括OutputStreamWriter,CharArrayWriter,F(xiàn)ileWriter等。Writer的write(int c)方法,會將傳入?yún)?shù)的低16位寫入到Writer中,忽略高16位的數(shù)據(jù)。
整合Writer和OutputStream
與Reader和InputStream類似,一個Writer可以和一個OutputStream相結合。把OutputStream包裝到OutputStreamWriter中,所有寫入到OutputStreamWriter的字符都將會傳遞給OutputStream。這是一個OutputStreamWriter的例子:
Writer writer = new OutputStreamWriter(outputStream);
Java IO中的管道為運行在同一個JVM中的兩個線程提供了通信的能力。所以管道也可以作為數(shù)據(jù)源以及目標媒介。
你不能利用管道與不同的JVM中的線程通信(不同的進程)。在概念上,Java的管道不同于Unix/Linux系統(tǒng)中的管道。在Unix/Linux中,運行在不同地址空間的兩個進程可以通過管道通信。在Java中,通信的雙方應該是運行在同一進程中的不同線程。
通過Java IO創(chuàng)建管道
可以通過Java IO中的PipedOutputStream和PipedInputStream創(chuàng)建管道。一個PipedInputStream流應該和一個PipedOutputStream流相關聯(lián)。 一個線程通過PipedOutputStream寫入的數(shù)據(jù)可以被另一個線程通過相關聯(lián)的PipedInputStream讀取出來。
Java IO管道示例
這是一個如何將PipedInputStream和PipedOutputStream關聯(lián)起來的簡單例子:
//使用管道來完成兩個線程間的數(shù)據(jù)點對點傳遞 @Test public void test2() throws IOException { PipedInputStream pipedInputStream = new PipedInputStream(); PipedOutputStream pipedOutputStream = new PipedOutputStream(pipedInputStream); new Thread(new Runnable() { @Override public void run() { try { pipedOutputStream.write("hello input".getBytes()); pipedOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { byte []arr = new byte[128]; while (pipedInputStream.read(arr) != -1) { System.out.println(Arrays.toString(arr)); } pipedInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }).start();
管道和線程
請記得,當使用兩個相關聯(lián)的管道流時,務必將它們分配給不同的線程。read()方法和write()方法調(diào)用時會導致流阻塞,這意味著如果你嘗試在一個線程中同時進行讀和寫,可能會導致線程死鎖。
管道的替代
除了管道之外,一個JVM中不同線程之間還有許多通信的方式。實際上,線程在大多數(shù)情況下會傳遞完整的對象信息而非原始的字節(jié)數(shù)據(jù)。但是,如果你需要在線程之間傳遞字節(jié)數(shù)據(jù),Java IO的管道是一個不錯的選擇。
Java中網(wǎng)絡的內(nèi)容或多或少的超出了Java IO的范疇。關于Java網(wǎng)絡更多的是在我的Java網(wǎng)絡教程中探討。但是既然網(wǎng)絡是一個常見的數(shù)據(jù)來源以及數(shù)據(jù)流目的地,并且因為你使用Java IO的API通過網(wǎng)絡連接進行通信,所以本文將簡要的涉及網(wǎng)絡應用。
當兩個進程之間建立了網(wǎng)絡連接之后,他們通信的方式如同操作文件一樣:利用InputStream讀取數(shù)據(jù),利用OutputStream寫入數(shù)據(jù)。換句話來說,Java網(wǎng)絡API用來在不同進程之間建立網(wǎng)絡連接,而Java IO則用來在建立了連接之后的進程之間交換數(shù)據(jù)。
基本上意味著如果你有一份能夠?qū)ξ募M行寫入某些數(shù)據(jù)的代碼,那么這些數(shù)據(jù)也可以很容易地寫入到網(wǎng)絡連接中去。你所需要做的僅僅只是在代碼中利用OutputStream替代FileOutputStream進行數(shù)據(jù)的寫入。因為FileOutputStream是OuputStream的子類,所以這么做并沒有什么問題。
//從網(wǎng)絡中讀取字節(jié)流也可以直接使用OutputStream public void test3() { //讀取網(wǎng)絡進程的輸出流 OutputStream outputStream = new OutputStream() { @Override public void write(int b) throws IOException { } }; } public void process(OutputStream ouput) throws IOException { //處理網(wǎng)絡信息 //do something with the OutputStream }
從InputStream或者Reader中讀入數(shù)組
從OutputStream或者Writer中寫數(shù)組
在java中常用字節(jié)和字符數(shù)組在應用中臨時存儲數(shù)據(jù)。而這些數(shù)組又是通常的數(shù)據(jù)讀取來源或者寫入目的地。如果你需要在程序運行時需要大量讀取文件里的內(nèi)容,那么你也可以把一個文件加載到數(shù)組中。
前面的例子中,字符數(shù)組或字節(jié)數(shù)組是用來緩存數(shù)據(jù)的臨時存儲空間,不過它們同時也可以作為數(shù)據(jù)來源或者寫入目的地。
舉個例子:
//字符數(shù)組和字節(jié)數(shù)組在io過程中的作用 public void test4() { //arr和brr分別作為數(shù)據(jù)源 char []arr = {'a','c','d'}; CharArrayReader charArrayReader = new CharArrayReader(arr); byte []brr = {1,2,3,4,5}; ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(brr); }
System.in, System.out, System.err這3個流同樣是常見的數(shù)據(jù)來源和數(shù)據(jù)流目的地。使用最多的可能是在控制臺程序里利用System.out將輸出打印到控制臺上。
JVM啟動的時候通過Java運行時初始化這3個流,所以你不需要初始化它們(盡管你可以在運行時替換掉它們)。
System.in System.in是一個典型的連接控制臺程序和鍵盤輸入的InputStream流。通常當數(shù)據(jù)通過命令行參數(shù)或者配置文件傳遞給命令行Java程序的時候,System.in并不是很常用。圖形界面程序通過界面?zhèn)鬟f參數(shù)給程序,這是一塊單獨的Java IO輸入機制。 System.out System.out是一個PrintStream流。System.out一般會把你寫到其中的數(shù)據(jù)輸出到控制臺上。System.out通常僅用在類似命令行工具的控制臺程序上。System.out也經(jīng)常用于打印程序的調(diào)試信息(盡管它可能并不是獲取程序調(diào)試信息的最佳方式)。 System.err System.err是一個PrintStream流。System.err與System.out的運行方式類似,但它更多的是用于打印錯誤文本。一些類似Eclipse的程序,為了讓錯誤信息更加顯眼,會將錯誤信息以紅色文本的形式通過System.err輸出到控制臺上。
System.out和System.err的簡單例子:
這是一個System.out和System.err結合使用的簡單示例:
//測試System.in, System.out, System.err public static void main(String[] args) { int in = new Scanner(System.in).nextInt(); System.out.println(in); System.out.println("out"); System.err.println("err"); //輸入10,結果是 // err(紅色) // 10 // out }
BufferedReader能為字符輸入流提供緩沖區(qū),可以提高許多IO處理的速度。你可以一次讀取一大塊的數(shù)據(jù),而不需要每次從網(wǎng)絡或者磁盤中一次讀取一個字節(jié)。特別是在訪問大量磁盤數(shù)據(jù)時,緩沖通常會讓IO快上許多。
BufferedReader和BufferedInputStream的主要區(qū)別在于,BufferedReader操作字符,而BufferedInputStream操作原始字節(jié)。只需要把Reader包裝到BufferedReader中,就可以為Reader添加緩沖區(qū)(譯者注:默認緩沖區(qū)大小為8192字節(jié),即8KB)。代碼如下:
Reader input = new BufferedReader(new FileReader("c:\\data\\input-file.txt"));
你也可以通過傳遞構造函數(shù)的第二個參數(shù),指定緩沖區(qū)大小,代碼如下:
Reader input = new BufferedReader(new FileReader("c:\\data\\input-file.txt"), 8 * 1024);
這個例子設置了8KB的緩沖區(qū)。最好把緩沖區(qū)大小設置成1024字節(jié)的整數(shù)倍,這樣能更高效地利用內(nèi)置緩沖區(qū)的磁盤。
除了能夠為輸入流提供緩沖區(qū)以外,其余方面BufferedReader基本與Reader類似。BufferedReader還有一個額外readLine()方法,可以方便地一次性讀取一整行字符。
BufferedWriter
與BufferedReader類似,BufferedWriter可以為輸出流提供緩沖區(qū)。可以構造一個使用默認大小緩沖區(qū)的BufferedWriter(譯者注:默認緩沖區(qū)大小8 * 1024B),代碼如下:
Writer writer = new BufferedWriter(new FileWriter("c:\\data\\output-file.txt"));
也可以手動設置緩沖區(qū)大小,代碼如下:
Writer writer = new BufferedWriter(new FileWriter("c:\\data\\output-file.txt"), 8 * 1024);
為了更好地使用內(nèi)置緩沖區(qū)的磁盤,同樣建議把緩沖區(qū)大小設置成1024的整數(shù)倍。除了能夠為輸出流提供緩沖區(qū)以外,其余方面BufferedWriter基本與Writer類似。類似地,BufferedWriter也提供了writeLine()方法,能夠把一行字符寫入到底層的字符輸出流中。
值得注意是,你需要手動flush()方法確保寫入到此輸出流的數(shù)據(jù)真正寫入到磁盤或者網(wǎng)絡中。
FilterReader
與FilterInputStream類似,F(xiàn)ilterReader是實現(xiàn)自定義過濾輸入字符流的基類,基本上它僅僅只是簡單覆蓋了Reader中的所有方法。
就我自己而言,我沒發(fā)現(xiàn)這個類明顯的用途。除了構造函數(shù)取一個Reader變量作為參數(shù)之外,我沒看到FilterReader任何對Reader新增或者修改的地方。如果你選擇繼承FilterReader實現(xiàn)自定義的類,同樣也可以直接繼承自Reader從而避免額外的類層級結構。
它是一種數(shù)據(jù)的流從源頭流到目的地。比如文件拷貝,輸入流和輸出流都包括了。輸入流從文件中讀取數(shù)據(jù)存儲到進程(process)中,輸出流從進程中讀取數(shù)據(jù)然后寫入到目標文件。
字節(jié)流在JDK1.0中就被引進了,用于操作包含ASCII字符的文件。JAVA也支持其他的字符如Unicode,為了讀取包含Unicode字符的文件,JAVA語言設計者在JDK1.1中引入了字符流。ASCII作為Unicode的子集,對于英語字符的文件,可以可以使用字節(jié)流也可以使用字符流。
java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer
這是在拷貝文件操作的時候,經(jīng)常用到的兩個類。在處理小文件的時候,它們性能表現(xiàn)還不錯,在大文件的時候,最好使用BufferedInputStream (或 BufferedReader) 和 BufferedOutputStream (或 BufferedWriter)
println是PrintStream的一個方法。out是一個靜態(tài)PrintStream類型的成員變量,System是一個java.lang包中的類,用于和底層的操作系統(tǒng)進行交互。
Filter Stream是一種IO流主要作用是用來對存在的流增加一些額外的功能,像給目標文件增加源文件中不存在的行數(shù),或者增加拷貝的性能。
在java.io包中主要由4個可用的filter Stream。兩個字節(jié)filter stream,兩個字符filter stream. 分別是FilterInputStream, FilterOutputStream, FilterReader and FilterWriter.這些類是抽象類,不能被實例化的。
在字節(jié)流的時候,使用BufferedInputStream和BufferedOutputStream。
在字符流的時候,使用BufferedReader 和 BufferedWriter
有四種管道流, PipedInputStream, PipedOutputStream, PipedReader 和 PipedWriter.在多個線程或進程中傳遞數(shù)據(jù)的時候管道流非常有用。
它不屬于 IO流,也不是用于文件操作的,它主要用于知道一個文件的屬性,讀寫權限,大小等信息。
它在java.io包中是一個特殊的類,既不是輸入流也不是輸出流,它兩者都可以做到。他是Object的直接子類。通常來說,一個流只有一個功能,要么讀,要么寫。但是RandomAccessFile既可以讀文件,也可以寫文件。 DataInputStream 和 DataOutStream有的方法,在RandomAccessFile中都存在。
到此,相信大家對“Java IO流常見面試題有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內(nèi)容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。