本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內容請到個人倉庫裏查看html
https://github.com/h2pl/Java-Tutorial前端
喜歡的話麻煩點下Star哈java
文章首發於個人我的博客:python
www.how2playlife.comgit
本文是微信公衆號【Java技術江湖】的《夯實Java基礎系列博文》其中一篇,本文部份內容來源於網絡,爲了把本文主題講得清晰透徹,也整合了不少我認爲不錯的技術博客內容,引用其中了一些比較好的博客文章,若有侵權,請聯繫做者。程序員
該系列博文會告訴你如何從入門到進階,一步步地學習Java基礎知識,並上手進行實戰,接着瞭解每一個Java知識點背後的實現原理,更完整地瞭解整個Java技術體系,造成本身的知識框架。爲了更好地總結和檢驗你的學習成果,本系列文章也會提供部分知識點對應的面試題以及參考答案。github
若是對本系列文章有什麼建議,或者是有什麼疑問的話,也能夠關注公衆號【Java技術江湖】聯繫做者,歡迎你參與本系列博文的創做和修訂。
面試
本文參考數據庫
併發編程網 – ifeve.com編程
在這一小節,我會試着給出Java IO(java.io)包下全部類的概述。更具體地說,我會根據類的用途對類進行分組。這個分組將會使你在將來的工做中,進行類的用途斷定時,或者是爲某個特定用途選擇類時變得更加容易。
輸入和輸出
術語「輸入」和「輸出」有時候會有一點讓人疑惑。一個應用程序的輸入每每是另一個應用程序的輸出 那麼OutputStream流究竟是一個輸出到目的地的流呢,仍是一個產生輸出的流?InputStream流到底會不會輸出它的數據給讀取數據的程序呢?就我我的而言,在第一天學習Java IO的時候我就感受到了一絲疑惑。 爲了消除這個疑惑,我試着給輸入和輸出起一些不同的別名,讓它們從概念上與數據的來源和數據的流向相聯繫。
Java的IO包主要關注的是從原始數據源的讀取以及輸出原始數據到目標媒介。如下是最典型的數據源和目標媒介:
文件 管道 網絡鏈接 內存緩存 System.in, System.out, System.error(注:Java標準輸入、輸出、錯誤輸出)
下面這張圖描繪了一個程序從數據源讀取數據,而後將數據輸出到其餘媒介的原理:
[外鏈圖片轉存失敗(img-VIwahDo3-1567839588993)(http://ifeve.com/wp-content/uploads/2014/10/%E6%97%A0%E6%A0%87%E9%A2%981.png)]
流
在Java IO中,流是一個核心的概念。流從概念上來講是一個連續的數據流。你既能夠從流中讀取數據,也能夠往流中寫數據。流與數據源或者數據流向的媒介相關聯。在Java IO中流既能夠是字節流(以字節爲單位進行讀寫),也能夠是字符流(以字符爲單位進行讀寫)。
類InputStream, OutputStream, Reader 和Writer
一個程序須要InputStream或者Reader從數據源讀取數據,須要OutputStream或者Writer將數據寫入到目標媒介中。如下的圖說明了這一點:
[外鏈圖片轉存失敗(img-dsCHgGDu-1567839588994)(http://ifeve.com/wp-content/uploads/2014/10/%E6%97%A0%E6%A0%87%E9%A2%982.png)]
InputStream和Reader與數據源相關聯,OutputStream和writer與目標媒介相關聯。
Java IO的用途和特徵
Java IO中包含了許多InputStream、OutputStream、Reader、Writer的子類。這樣設計的緣由是讓每個類都負責不一樣的功能。這也就是爲何IO包中有這麼多不一樣的類的緣故。各種用途彙總以下:
文件訪問 網絡訪問 內存緩存訪問 線程內部通訊(管道) 緩衝 過濾 解析 讀寫文本 (Readers / Writers) 讀寫基本類型數據 (long, int etc.) 讀寫對象
當通讀過Java IO類的源代碼以後,咱們很容易就能瞭解這些用途。這些用途或多或少讓咱們更加容易地理解,不一樣的類用於針對不一樣業務場景。
Java IO類概述表
已經討論了數據源、目標媒介、輸入、輸出和各種不一樣用途的Java IO類,接下來是一張經過輸入、輸出、基於字節或者字符、以及其餘好比緩衝、解析之類的特定用途劃分的大部分Java IO類的表格。
[外鏈圖片轉存失敗(img-a3UYZ3ow-1567839588995)(http://ifeve.com/wp-content/uploads/2014/10/QQ%E6%88%AA%E5%9B%BE20141020174145.png)]
Java IO類圖
[外鏈圖片轉存失敗(img-n1TqKmEv-1567839588995)(https://images.cnblogs.com/cnblogs_com/davidgu/java_io_hierarchy.jpg)]
Java IO流是既能夠從中讀取,也能夠寫入到其中的數據流。正如這個系列教程以前提到過的,流一般會與數據源、數據流向目的地相關聯,好比文件、網絡等等。
流和數組不同,不能經過索引讀寫數據。在流中,你也不能像數組那樣先後移動讀取數據,除非使用RandomAccessFile 處理文件。流僅僅只是一個連續的數據流。
某些相似PushbackInputStream 流的實現容許你將數據從新推回到流中,以便從新讀取。然而你只能把有限的數據推回流中,而且你不能像操做數組那樣隨意讀取數據。流中的數據只可以順序訪問。
Java IO流一般是基於字節或者基於字符的。字節流一般以「stream」命名,好比InputStream和OutputStream。除了DataInputStream 和DataOutputStream 還可以讀寫int, long, float和double類型的值之外,其餘流在一個操做時間內只能讀取或者寫入一個原始字節。
字符流一般以「Reader」或者「Writer」命名。字符流可以讀寫字符(好比Latin1或者Unicode字符)。能夠瀏覽Java Readers and Writers獲取更多關於字符流輸入輸出的信息。
InputStream
java.io.InputStream類是全部Java IO輸入流的基類。若是你正在開發一個從流中讀取數據的組件,請嘗試用InputStream替代任何它的子類(好比FileInputStream)進行開發。這麼作可以讓你的代碼兼容任何類型而非某種肯定類型的輸入流。
組合流
你能夠將流整合起來以便實現更高級的輸入和輸出操做。好比,一次讀取一個字節是很慢的,因此能夠從磁盤中一次讀取一大塊數據,而後從讀到的數據塊中獲取字節。爲了實現緩衝,能夠把InputStream包裝到BufferedInputStream中。
代碼示例
InputStream input = new BufferedInputStream(new FileInputStream("c:\data\input-file.txt"));
緩衝一樣能夠應用到OutputStream中。你能夠實現將大塊數據批量地寫入到磁盤(或者相應的流)中,這個功能由BufferedOutputStream實現。
緩衝只是經過流整合實現的其中一個效果。你能夠把InputStream包裝到PushbackInputStream中,以後能夠將讀取過的數據推回到流中從新讀取,在解析過程當中有時候這樣作很方便。或者,你能夠將兩個InputStream整合成一個SequenceInputStream。
將不一樣的流整合到一個鏈中,能夠實現更多種高級操做。經過編寫包裝了標準流的類,能夠實現你想要的效果和過濾器。
在Java應用程序中,文件是一種經常使用的數據源或者存儲數據的媒介。因此這一小節將會對Java中文件的使用作一個簡短的概述。這篇文章不會對每個技術細節都作出解釋,而是會針對文件存取的方法提供給你一些必要的知識點。在以後的文章中,將會更加詳細地描述這些方法或者類,包括方法示例等等。
經過Java IO讀文件
若是你須要在不一樣端之間讀取文件,你能夠根據該文件是二進制文件仍是文本文件來選擇使用FileInputStream或者FileReader。 這兩個類容許你從文件開始到文件末尾一次讀取一個字節或者字符,或者將讀取到的字節寫入到字節數組或者字符數組。你沒必要一次性讀取整個文件,相反你能夠按順序地讀取文件中的字節和字符。
若是你須要跳躍式地讀取文件其中的某些部分,可使用RandomAccessFile。
經過Java IO寫文件
若是你須要在不一樣端之間進行文件的寫入,你能夠根據你要寫入的數據是二進制型數據仍是字符型數據選用FileOutputStream或者FileWriter。 你能夠一次寫入一個字節或者字符到文件中,也能夠直接寫入一個字節數組或者字符數據。數據按照寫入的順序存儲在文件當中。
經過Java IO隨機存取文件
正如我所提到的,你能夠經過RandomAccessFile對文件進行隨機存取。
隨機存取並不意味着你能夠在真正隨機的位置進行讀寫操做,它只是意味着你能夠跳過文件中某些部分進行操做,而且支持同時讀寫,不要求特定的存取順序。 這使得RandomAccessFile能夠覆蓋一個文件的某些部分、或者追加內容到它的末尾、或者刪除它的某些內容,固然它也能夠從文件的任何位置開始讀取文件。
下面是具體例子:
@Test //文件流範例,打開一個文件的輸入流,讀取到字節數組,再寫入另外一個文件的輸出流 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參數來決定讀或者寫 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是基於字節的,還記得嗎?
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,FileReader等等。能夠查看Java IO概述瀏覽完整的Reader表格。
整合Reader與InputStream
一個Reader能夠和一個InputStream相結合。若是你有一個InputStream輸入流,而且想從其中讀取字符,能夠把這個InputStream包裝到InputStreamReader中。把InputStream傳遞到InputStreamReader的構造函數中:
Reader reader = new InputStreamReader(inputStream);
在構造函數中能夠指定解碼方式。
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,由於子類的實現更加明確,更能表現你的意圖。經常使用子類包括OutputStreamWriter,CharArrayWriter,FileWriter等。Writer的write(int c)方法,會將傳入參數的低16位寫入到Writer中,忽略高16位的數據。
整合Writer和OutputStream
與Reader和InputStream相似,一個Writer能夠和一個OutputStream相結合。把OutputStream包裝到OutputStreamWriter中,全部寫入到OutputStreamWriter的字符都將會傳遞給OutputStream。這是一個OutputStreamWriter的例子:
Writer writer = new OutputStreamWriter(outputStream);
Java IO中的管道爲運行在同一個JVM中的兩個線程提供了通訊的能力。因此管道也能夠做爲數據源以及目標媒介。
你不能利用管道與不一樣的JVM中的線程通訊(不一樣的進程)。在概念上,Java的管道不一樣於Unix/Linux系統中的管道。在Unix/Linux中,運行在不一樣地址空間的兩個進程能夠經過管道通訊。在Java中,通訊的雙方應該是運行在同一進程中的不一樣線程。
經過Java IO建立管道
能夠經過Java IO中的PipedOutputStream和PipedInputStream建立管道。一個PipedInputStream流應該和一個PipedOutputStream流相關聯。 一個線程經過PipedOutputStream寫入的數據能夠被另外一個線程經過相關聯的PipedInputStream讀取出來。
Java IO管道示例
這是一個如何將PipedInputStream和PipedOutputStream關聯起來的簡單例子:
//使用管道來完成兩個線程間的數據點對點傳遞 @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();
管道和線程
請記得,當使用兩個相關聯的管道流時,務必將它們分配給不一樣的線程。read()方法和write()方法調用時會致使流阻塞,這意味着若是你嘗試在一個線程中同時進行讀和寫,可能會致使線程死鎖。
管道的替代
除了管道以外,一個JVM中不一樣線程之間還有許多通訊的方式。實際上,線程在大多數狀況下會傳遞完整的對象信息而非原始的字節數據。可是,若是你須要在線程之間傳遞字節數據,Java IO的管道是一個不錯的選擇。
Java中網絡的內容或多或少的超出了Java IO的範疇。關於Java網絡更多的是在個人Java網絡教程中探討。可是既然網絡是一個常見的數據來源以及數據流目的地,而且由於你使用Java IO的API經過網絡鏈接進行通訊,因此本文將簡要的涉及網絡應用。
當兩個進程之間創建了網絡鏈接以後,他們通訊的方式如同操做文件同樣:利用InputStream讀取數據,利用OutputStream寫入數據。換句話來講,Java網絡API用來在不一樣進程之間創建網絡鏈接,而Java IO則用來在創建了鏈接以後的進程之間交換數據。
基本上意味着若是你有一份可以對文件進行寫入某些數據的代碼,那麼這些數據也能夠很容易地寫入到網絡鏈接中去。你所須要作的僅僅只是在代碼中利用OutputStream替代FileOutputStream進行數據的寫入。由於FileOutputStream是OuputStream的子類,因此這麼作並無什麼問題。
//從網絡中讀取字節流也能夠直接使用OutputStream public void test3() { //讀取網絡進程的輸出流 OutputStream outputStream = new OutputStream() { @Override public void write(int b) throws IOException { } }; } public void process(OutputStream ouput) throws IOException { //處理網絡信息 //do something with the OutputStream }
從InputStream或者Reader中讀入數組
從OutputStream或者Writer中寫數組
在java中經常使用字節和字符數組在應用中臨時存儲數據。而這些數組又是一般的數據讀取來源或者寫入目的地。若是你須要在程序運行時須要大量讀取文件裏的內容,那麼你也能夠把一個文件加載到數組中。
前面的例子中,字符數組或字節數組是用來緩存數據的臨時存儲空間,不過它們同時也能夠做爲數據來源或者寫入目的地。
舉個例子:
//字符數組和字節數組在io過程當中的做用 public void test4() { //arr和brr分別做爲數據源 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個流一樣是常見的數據來源和數據流目的地。使用最多的多是在控制檯程序裏利用System.out將輸出打印到控制檯上。
JVM啓動的時候經過Java運行時初始化這3個流,因此你不須要初始化它們(儘管你能夠在運行時替換掉它們)。
System.in System.in是一個典型的鏈接控制檯程序和鍵盤輸入的InputStream流。一般當數據經過命令行參數或者配置文件傳遞給命令行Java程序的時候,System.in並非很經常使用。圖形界面程序經過界面傳遞參數給程序,這是一塊單獨的Java IO輸入機制。 System.out System.out是一個PrintStream流。System.out通常會把你寫到其中的數據輸出到控制檯上。System.out一般僅用在相似命令行工具的控制檯程序上。System.out也常常用於打印程序的調試信息(儘管它可能並非獲取程序調試信息的最佳方式)。 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能爲字符輸入流提供緩衝區,能夠提升許多IO處理的速度。你能夠一次讀取一大塊的數據,而不須要每次從網絡或者磁盤中一次讀取一個字節。特別是在訪問大量磁盤數據時,緩衝一般會讓IO快上許多。
BufferedReader和BufferedInputStream的主要區別在於,BufferedReader操做字符,而BufferedInputStream操做原始字節。只須要把Reader包裝到BufferedReader中,就能夠爲Reader添加緩衝區(譯者注:默認緩衝區大小爲8192字節,即8KB)。代碼以下:
Reader input = new BufferedReader(new FileReader("c:\\data\\input-file.txt"));
你也能夠經過傳遞構造函數的第二個參數,指定緩衝區大小,代碼以下:
Reader input = new BufferedReader(new FileReader("c:\\data\\input-file.txt"), 8 * 1024);
這個例子設置了8KB的緩衝區。最好把緩衝區大小設置成1024字節的整數倍,這樣能更高效地利用內置緩衝區的磁盤。
除了可以爲輸入流提供緩衝區之外,其他方面BufferedReader基本與Reader相似。BufferedReader還有一個額外readLine()方法,能夠方便地一次性讀取一整行字符。
BufferedWriter
與BufferedReader相似,BufferedWriter能夠爲輸出流提供緩衝區。能夠構造一個使用默認大小緩衝區的BufferedWriter(譯者注:默認緩衝區大小8 * 1024B),代碼以下:
Writer writer = new BufferedWriter(new FileWriter("c:\\data\\output-file.txt"));
也能夠手動設置緩衝區大小,代碼以下:
Writer writer = new BufferedWriter(new FileWriter("c:\\data\\output-file.txt"), 8 * 1024);
爲了更好地使用內置緩衝區的磁盤,一樣建議把緩衝區大小設置成1024的整數倍。除了可以爲輸出流提供緩衝區之外,其他方面BufferedWriter基本與Writer相似。相似地,BufferedWriter也提供了writeLine()方法,可以把一行字符寫入到底層的字符輸出流中。
值得注意是,你須要手動flush()方法確保寫入到此輸出流的數據真正寫入到磁盤或者網絡中。
FilterReader
與FilterInputStream相似,FilterReader是實現自定義過濾輸入字符流的基類,基本上它僅僅只是簡單覆蓋了Reader中的全部方法。
就我本身而言,我沒發現這個類明顯的用途。除了構造函數取一個Reader變量做爲參數以外,我沒看到FilterReader任何對Reader新增或者修改的地方。若是你選擇繼承FilterReader實現自定義的類,一樣也能夠直接繼承自Reader從而避免額外的類層級結構。
它是一種數據的流從源頭流到目的地。好比文件拷貝,輸入流和輸出流都包括了。輸入流從文件中讀取數據存儲到進程(process)中,輸出流從進程中讀取數據而後寫入到目標文件。
字節流在JDK1.0中就被引進了,用於操做包含ASCII字符的文件。JAVA也支持其餘的字符如Unicode,爲了讀取包含Unicode字符的文件,JAVA語言設計者在JDK1.1中引入了字符流。ASCII做爲Unicode的子集,對於英語字符的文件,能夠可使用字節流也可使用字符流。
java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer
這是在拷貝文件操做的時候,常常用到的兩個類。在處理小文件的時候,它們性能表現還不錯,在大文件的時候,最好使用BufferedInputStream (或 BufferedReader) 和 BufferedOutputStream (或 BufferedWriter)
println是PrintStream的一個方法。out是一個靜態PrintStream類型的成員變量,System是一個java.lang包中的類,用於和底層的操做系統進行交互。
Filter Stream是一種IO流主要做用是用來對存在的流增長一些額外的功能,像給目標文件增長源文件中不存在的行數,或者增長拷貝的性能。
在java.io包中主要由4個可用的filter Stream。兩個字節filter stream,兩個字符filter stream. 分別是FilterInputStream, FilterOutputStream, FilterReader and FilterWriter.這些類是抽象類,不能被實例化的。
在字節流的時候,使用BufferedInputStream和BufferedOutputStream。
在字符流的時候,使用BufferedReader 和 BufferedWriter
有四種管道流, PipedInputStream, PipedOutputStream, PipedReader 和 PipedWriter.在多個線程或進程中傳遞數據的時候管道流很是有用。
它不屬於 IO流,也不是用於文件操做的,它主要用於知道一個文件的屬性,讀寫權限,大小等信息。
它在java.io包中是一個特殊的類,既不是輸入流也不是輸出流,它二者均可以作到。他是Object的直接子類。一般來講,一個流只有一個功能,要麼讀,要麼寫。可是RandomAccessFile既能夠讀文件,也能夠寫文件。 DataInputStream 和 DataOutStream有的方法,在RandomAccessFile中都存在。
https://www.imooc.com/article/24305
https://www.cnblogs.com/UncleWang001/articles/10454685.html
https://www.cnblogs.com/Jixiangwei/p/Java.html
https://blog.csdn.net/baidu_37107022/article/details/76890019
黃小斜是 985 碩士,阿里巴巴Java工程師,在自學編程、技術求職、Java學習等方面有豐富經驗和獨到看法,但願幫助到更多想要從事互聯網行業的程序員們。
做者專一於 JAVA 後端技術棧,熱衷於分享程序員乾貨、學習經驗、求職心得,以及自學編程和Java技術棧的相關乾貨。
黃小斜是一個斜槓青年,堅持學習和寫做,相信終身學習的力量,但願和更多的程序員交朋友,一塊兒進步和成長!
原創電子書:
關注微信公衆號【程序員黃小斜】後回覆【原創電子書】便可領取我原創的電子書《菜鳥程序員修煉手冊:從技術小白到阿里巴巴Java工程師》這份電子書總結了我2年的Java學習之路,包括學習方法、技術總結、求職經驗和麪試技巧等內容,已經幫助不少的程序員拿到了心儀的offer!
程序員3T技術學習資源: 一些程序員學習技術的資源大禮包,關注公衆號後,後臺回覆關鍵字 「資料」 便可免費無套路獲取,包括Java、python、C++、大數據、機器學習、前端、移動端等方向的技術資料。同時也包括我原創的【程序員校招大禮包】、【求職面試大禮包】等內容贈送,都是各位求職或者面試路上小夥伴很是實用的內容。
若是你們想要實時關注我更新的文章以及分享的乾貨的話,能夠關注個人微信公衆號【Java技術江湖】
這是一位阿里 Java 工程師的技術小站。做者黃小斜,專一 Java 相關技術:SSM、SpringBoot、MySQL、分佈式、中間件、集羣、Linux、網絡、多線程,偶爾講點Docker、ELK,同時也分享技術乾貨和學習經驗,致力於Java全棧開發!
Java工程師必備學習資源: 關注公衆號【Java技術江湖】後回覆 Java 便可領取 Java基礎、進階、項目和架構師等免費學習資料,更有數據庫、分佈式、微服務等熱門技術學習視頻,內容豐富,兼顧原理和實踐,另外也將贈送做者原創的Java學習指南、Java程序員面試指南等乾貨資源