一文講透Java面試必考點(diǎn):IO流
![]()
Java大聯(lián)盟 致力于最高效的Java學(xué)習(xí)
關(guān)注
原文鏈接 https://cloud.tencent.com/developer/article/2318311
選題思路
每當(dāng)有小白學(xué)習(xí)JAVA基礎(chǔ)的IO流時(shí),總是一看就會(huì),一寫(xiě)就廢,就算學(xué)過(guò)后過(guò)段時(shí)間也會(huì)忘記,特別是在每次要用時(shí),還得上度娘,這種現(xiàn)象其實(shí)就說(shuō)明還是對(duì)IO流的理解深度不夠。
所以今天將以下原則,關(guān)鍵點(diǎn),還有邏輯明確,面試時(shí)徒手?jǐn)]IO就不是夢(mèng)。
創(chuàng)作提綱
1.玩轉(zhuǎn)IO四條基本原則
2.File類是什么
3.什么是IO
4.字節(jié)流與字符流的愛(ài)恨情仇
5.緩沖流
6.轉(zhuǎn)換流
1.四條基本原則
在動(dòng)手?jǐn)]代碼前,了解需求是必要的,了解完分析實(shí)現(xiàn)思路,無(wú)論再怎么復(fù)雜按照以下四條思路來(lái)寫(xiě)準(zhǔn)沒(méi)錯(cuò)。
(1) 確定數(shù)據(jù)是數(shù)據(jù)源還是數(shù)據(jù)目的,就是說(shuō)我們要將這些數(shù)據(jù)是從磁盤(pán)讀出還是寫(xiě)入
(2) 確定你要操作的數(shù)據(jù)是文本還是字節(jié),可以提高處理效率。
(3) 確定數(shù)據(jù)所在的設(shè)備。
(4) 確定有無(wú)讀寫(xiě)效率上的需求,是否要使用到緩沖流,轉(zhuǎn)換流等。
2.File類是什么
java.io.File IO操作中使用最頻繁的類,也特別重要,它很好理解只是代表我們要操作的文件夾或者文件對(duì)象,但是File跟流無(wú)關(guān),所以File類不能直接對(duì)文件數(shù)據(jù)進(jìn)行讀和寫(xiě)也就是輸入和輸出。要注意的點(diǎn):
1.一個(gè)File對(duì)象代表硬盤(pán)中實(shí)際存在的一個(gè)文件或者目錄。
2.File類構(gòu)造方法不會(huì)給你檢驗(yàn)這個(gè)文件或文件夾是否真實(shí)存在,因此無(wú)論該路徑下是否存在文件或者目錄,都不影響File對(duì)象的創(chuàng)建。
public static void main(String[] args) {//代表文件File f = new File("c:\\aa\\bb.java");//代表文件夾File f1 = new File("c:\\aa");}}3.什么是IO
可以簡(jiǎn)單理解為數(shù)據(jù)的流動(dòng),JAVA中的具體體現(xiàn)就是java.io包下的那些類用來(lái)對(duì)數(shù)據(jù)進(jìn)行輸入(讀取數(shù)據(jù))輸出(寫(xiě)出數(shù)據(jù)),就是IO操作。
可以大致分為一下幾類:
1.輸入流:將數(shù)據(jù)從其他設(shè)備(如硬盤(pán))上讀取到內(nèi)存上的流
2.輸出流:把內(nèi)存中的數(shù)據(jù)寫(xiě)到其他設(shè)備(也可以是硬盤(pán))上的流。
3.字節(jié)流:以字節(jié)為最小單位,進(jìn)行讀寫(xiě)操作字節(jié)數(shù)據(jù)的流。
4.字符流:以字符為最小單位,進(jìn)行讀寫(xiě)操作字符數(shù)據(jù)的流。
根據(jù)以上分類可分成以下超級(jí)父類:字節(jié)輸入流 InputStream,字節(jié)輸出流 OutputStream,字符輸入流 Reader,字符輸出流 Writer。其他常用的類都是這些超類的子類。
重點(diǎn):計(jì)算機(jī)中所有的文件數(shù)據(jù)不管是文本,音頻視頻還是圖片,全都是保存為二進(jìn)制的數(shù)據(jù),所以都是一個(gè)又一個(gè)的字節(jié),所以其實(shí)字節(jié)流可以傳輸任何的文件數(shù)據(jù),(其實(shí)字符流的底層也是傳輸二進(jìn)制數(shù)據(jù),只是如果用字節(jié)流還要對(duì)照編碼表去翻譯,不然會(huì)亂碼)。
4.字節(jié)流與字符流的愛(ài)恨情仇
字節(jié)輸入流IputStream:
<font color="red">基本方法功能:</font>
(1) public void close() :關(guān)閉此輸入流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。
(2)public abstract int read():從輸入流讀取數(shù)據(jù)的下一個(gè)字節(jié)。
(3) public int read(byte[] b):該方法返回的int值代表的是讀取了多少個(gè)字節(jié),讀到幾個(gè)返回幾個(gè),讀取不到返回-1
FileInputStream類是文件輸入流,從文件中讀取字節(jié),也是最常用的,繼承inputStream。
<font color="blue">構(gòu)造方法:</font>(當(dāng)你創(chuàng)建這個(gè)流對(duì)象時(shí),如果你指定的路徑下沒(méi)有該文件,就會(huì)拋出FileNotFoundException異常)
1、 FileInputStream(File file):通過(guò)打開(kāi)與實(shí)際文件的連接來(lái)創(chuàng)建一個(gè) FileInputStream ,該文件由文件系統(tǒng)中的 File對(duì)象 file命名。
2、 FileInputStream(String name):通過(guò)打開(kāi)與實(shí)際文件的連接來(lái)創(chuàng)建一個(gè) FileInputStream ,該文件由文件系統(tǒng)中的路徑名name命名。(推薦)
<font color="blue">常用方式:</font>
package io;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class input2 {public static void main(String args[]){FileInputStream inputStream = null;try {inputStream = new FileInputStream("a.txt");//創(chuàng)建流對(duì)象int len = 0 ;byte[] bys = new byte[1024];//緩沖數(shù)組while ((len = inputStream.read(bys)) != -1) {//循環(huán)讀取數(shù)據(jù)System.out.println(new String(bys,0,len));}} catch (IOException e) {e.printStackTrace();}finally {try {inputStream.close();//關(guān)閉流} catch (IOException e) {e.printStackTrace();}}}}字節(jié)輸出流OutputStream:
<font color="red">基本方法功能:</font>
(1) public void close() :關(guān)閉此輸出流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。
(2) public void flush() :刷新此輸出流并強(qiáng)制任何緩沖的輸出字節(jié)被寫(xiě)出。
(3 )public void write(byte[] b):將 b.length個(gè)字節(jié)從指定的字節(jié)數(shù)組寫(xiě)入此輸出流。
(4) public void write(byte[] b, int off, int len) :從指定的字節(jié)數(shù)組寫(xiě)入 len字節(jié),從偏移量 off開(kāi)始輸出到此輸出流。也就是說(shuō)從off個(gè)字節(jié)數(shù)開(kāi)始讀取一直到len個(gè)字節(jié)結(jié)束
(5) public abstract void write(int b) :將指定的字節(jié)輸出流。
FileOutputStream類是文件輸出流,將字節(jié)寫(xiě)入文件,也是最常用的,繼承outputStream。
<font color="blue">構(gòu)造方法:</font>(當(dāng)你創(chuàng)建這個(gè)流對(duì)象時(shí),如果你指定的路徑下沒(méi)有該文件,就會(huì)自動(dòng)給你新建一個(gè))
1、 public FileOutputStream(File file):根據(jù)File對(duì)象為參數(shù)創(chuàng)建對(duì)象。
2、 public FileOutputStream(String name):根據(jù)名稱字符串為參數(shù)創(chuàng)建對(duì)象。(推薦)
<font color="blue">常用方式:</font>
public class FOSWrite {public static void main(String[] args) throws IOException {// 使用文件名稱創(chuàng)建流對(duì)象FileOutputStream fos = new FileOutputStream("a.txt");// 字符串轉(zhuǎn)換為字節(jié)數(shù)組byte[] b = "可惡的老六".getBytes();// 寫(xiě)出字節(jié)數(shù)組數(shù)據(jù)fos.write(b);// 關(guān)閉資源fos.close();}}輸出結(jié)果:可惡的老六字符輸入流Reader:
<font color="red">基本方法功能:</font>
(1)public void close() :關(guān)閉此流并釋放與此流相關(guān)聯(lián)的任何系統(tǒng)資源。
(2) public int read():從輸入流讀取一個(gè)字符。
(3) public int read(char[] cbuf):從輸入流中讀取一些字符,并將它們存儲(chǔ)到字符數(shù)組 cbuf中
FileReader類是讀取字符文件的便利類。構(gòu)造時(shí)使用系統(tǒng)默認(rèn)的字符編碼和默認(rèn)字節(jié)緩沖區(qū)。
<font color="blue">構(gòu)造方法:</font>
1、FileReader(File file):創(chuàng)建一個(gè)新的 FileReader ,給定要讀取的File對(duì)象。
2、 FileReader(String fileName):創(chuàng)建一個(gè)新的 FileReader ,給定要讀取的文件的字符串名稱。
<font color="blue">常用方式:</font>
public class FRRead {public static void main(String[] args) throws IOException {// 使用文件名稱創(chuàng)建流對(duì)象FileReader fr = new FileReader("a.txt");// 定義變量,保存數(shù)據(jù)int b ;// 循環(huán)讀取while ((b = fr.read())!=-1) {System.out.println((char)b);}// 關(guān)閉資源fr.close();}}字符輸出流 Writer:
<font color="red">基本方法功能:</font>
(1)void write(int c) 寫(xiě)入單個(gè)字符。
(2)void write(char[] cbuf)寫(xiě)入字符數(shù)組。
(3) abstract void write(char[] cbuf, int off, int len)寫(xiě)入字符數(shù)組的某一部分,off數(shù)組的開(kāi)始索引,len寫(xiě)的字符個(gè)數(shù)。
(4) void write(String str)寫(xiě)入字符串。
(5)void write(String str, int off, int len) 寫(xiě)入字符串的某一部分,off字符串的開(kāi)始索引,len寫(xiě)的字符個(gè)數(shù)。
(6)void flush()刷新該流的緩沖。
(7)void close() 關(guān)閉此流,但要先刷新它。
FileWriter類是寫(xiě)出字符到文件的便利類。構(gòu)造時(shí)使用系統(tǒng)默認(rèn)的字符編碼和默認(rèn)字節(jié)緩沖區(qū)。
<font color="blue">構(gòu)造方法:</font>
1、 FileWriter(File file):創(chuàng)建一個(gè)新的 FileWriter,給定要讀取的File對(duì)象。
2、FileWriter(String fileName):創(chuàng)建一個(gè)新的 FileWriter,給定要讀取的文件的名稱。
<font color="blue">常用方式:</font>
public class FlushDemo {public static void main(String[] args) throws Exception {//源 也就是輸入流【讀取流】 讀取a.txt文件FileReader fr=new FileReader("a.txt"); //必須要存在a.txt文件,否則報(bào)FileNotFoundException異常//目的地 也就是輸出流FileWriter fw=new FileWriter("b.txt"); //沒(méi)有會(huì)自動(dòng)創(chuàng)建b.txt,int len;while((len=fr.read())!=-1){fw.write(len);}fr.close();fw.flush();fw.close();}}<font color="red">【注意】:</font>關(guān)閉資源時(shí),與FileOutputStream不同。仔細(xì)看這里的close()方法,并沒(méi)有進(jìn)行刷新,所以要先使用flush()方法刷新緩存,再用close()方法關(guān)閉流對(duì)象。
5.緩沖流
緩沖流也叫高效是對(duì)以上四個(gè)流的效率增強(qiáng)流。
字節(jié)緩沖流:
BufferedInputStream,BufferedOutputStream字符緩沖流:
BufferedReader,BufferedWriter<font color="red">基本原理:</font>
1、使用了底層流對(duì)象從具體設(shè)備上獲取數(shù)據(jù),并將數(shù)據(jù)存儲(chǔ)到緩沖區(qū)的數(shù)組內(nèi)。
2、通過(guò)緩沖區(qū)的read()方法從緩沖區(qū)獲取具體的字符數(shù)據(jù),這樣就提高了效率。
3、如果用read方法讀取字符數(shù)據(jù),并存儲(chǔ)到另一個(gè)容器中,直到讀取到了換行符時(shí),將另一個(gè)容器臨時(shí)存儲(chǔ)的數(shù)據(jù)轉(zhuǎn)成字符串返回,就形成了readLine()功能。
<font color="blue">構(gòu)造方法:</font>
BufferedInputStream(InputStream in) :創(chuàng)建一個(gè)新的緩沖輸入流,注意參數(shù)類型為InputStream。
BufferedOutputStream(OutputStream out):創(chuàng)建一個(gè)新的緩沖輸出流,注意參數(shù)類型為OutputStream。
public BufferedReader(Reader in) :創(chuàng)建一個(gè)新的緩沖輸入流,注意參數(shù)類型為Reader。
public BufferedWriter(Writer out):創(chuàng)建一個(gè)新的緩沖輸出流,注意參數(shù)類型為Writer。
<font color="blue">常用方式:</font>
public class BufferedTest {public static void main(String[] args) throws IOException {HashMap<String, String> map = new HashMap<>();// 創(chuàng)建流對(duì)象 源BufferedReader br = new BufferedReader(new FileReader("a.txt"));//目標(biāo)BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));// 讀取數(shù)據(jù)String line = null;while ((line = br.readLine())!=null) {// 解析文本String[] split = line.split("\\.");// 保存到集合map.put(split[0],split[1]);}// 釋放資源br.close();// 遍歷map集合for (int i = 1; i <= map.size(); i++) {String key = String.valueOf(i);// 獲取map中文本String value = map.get(key);// 寫(xiě)出拼接文本bw.write(key+"."+value);// 寫(xiě)出換行bw.newLine();}// 釋放資源bw.close();}}6.轉(zhuǎn)換流
先了解字符編碼跟字符集這兩個(gè)概念
編碼解碼:計(jì)算機(jī)中儲(chǔ)存的信息都是用二進(jìn)制數(shù)表示的,而我們?cè)谄聊簧峡吹降臄?shù)字、英文、標(biāo)點(diǎn)符號(hào)、漢字等字符是二進(jìn)制數(shù)轉(zhuǎn)換之后的結(jié)果。按照某種規(guī)則,將字符存儲(chǔ)到計(jì)算機(jī)中,稱為編碼 。同樣的道理有編碼就有解碼,就是把這些二進(jìn)制數(shù)據(jù)按照一定的規(guī)則解析成我們能看懂的東西。
字符集:也叫編碼表。是一個(gè)系統(tǒng)支持的所有字符的集合,包括各國(guó)家文字、標(biāo)點(diǎn)符號(hào)、圖形符號(hào)、數(shù)字等這些包含各種各樣規(guī)則的集合。(萬(wàn)國(guó)語(yǔ)翻譯器)
<font color="red">基本原理:</font>字符流=字節(jié)流+編碼表
InputStreamReader類(字節(jié)流到字符流轉(zhuǎn)換)
<font color="blue">構(gòu)造方法:</font>
InputStreamReader(InputStream in): 創(chuàng)建一個(gè)使用默認(rèn)字符集的字符流。
InputStreamReader(InputStream in, String charsetName): 創(chuàng)建一個(gè)指定字符集的字符流。
<font color="blue">常用方式:</font>
public class ReaderDemo2 {public static void main(String[] args) throws IOException {// 定義文件路徑,文件為gbk編碼String FileName = "C:\\a.txt";// 創(chuàng)建流對(duì)象,默認(rèn)UTF8編碼InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));// 創(chuàng)建流對(duì)象,指定GBK編碼InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK");// 定義變量,保存字符int read;// 使用默認(rèn)編碼字符流讀取,亂碼while ((read = isr.read()) != -1) {System.out.print((char)read); // ??????}isr.close();// 使用指定編碼字符流讀取,正常解析while ((read = isr2.read()) != -1) {System.out.print((char)read);}isr2.close();}}OutputStreamWriter類(字符流到字節(jié)流轉(zhuǎn)換)
<font color="blue">構(gòu)造方法:</font>
OutputStreamWriter(OutputStream in): 創(chuàng)建一個(gè)使用默認(rèn)字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName): 創(chuàng)建一個(gè)指定字符集的字符流。
<font color="blue">常用方式:</font>
-
public class OutputDemo {public static void main(String[] args) throws IOException {// 定義文件路徑String FileName = "C:\\s.txt";// 創(chuàng)建流對(duì)象,默認(rèn)UTF8編碼OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));// 寫(xiě)出數(shù)據(jù)osw.write("老六"); // 保存為6個(gè)字節(jié)osw.close();// 定義文件路徑String FileName2 = "C:\\a.txt";// 創(chuàng)建流對(duì)象,指定GBK編碼OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");// 寫(xiě)出數(shù)據(jù)osw2.write("受死");// 保存為4個(gè)字節(jié)osw2.close();}}

