Java基础之IO流(持续更新中)
----------------------------------------------------------------------------------------
按數據內容分:字節流和字符流按流向分:輸入流(硬盤到內存)和輸出流(內存到硬盤)字節流:因為計算機是以二進制的方式進行運算的,所以字節流能處理所有數據字符流:一開始是沒有字符流的
大家可以從源碼中看到發布的版本,字符流的原理是讀到數據后不直接處理,而是參照碼表后獲取對應的文字,然后再對文字進行操作,可以理解成 字符流=字節流+碼表,如果你想操作文字字符串類型的數據,那毫無疑問,優先考慮字符流沒錯~
-----------------------------------------------------------------------------------------------------------
IO流字符流的讀寫
接下來看一下Java提供了哪些技術來用于IO操作 首先提供了抽象類字節流的兩個父類:InputStream,OutputStream字符流的兩個父類:Reader,Writer因為不斷向上抽取,所以功能變得不具體了,很抽象,形成了抽象類。所以要看父類,一看就知道是做什么用的。用子類,子類有很多具體的功能。
大家對文本應該都比較熟悉,所以先來介紹一下Writer的子類FileWriter,這些體系的子類都以父類名作為后綴,前綴就是該對象的功能,比如FileWriter,一看就知道是寫文件的便捷類。FileWriter import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo {private static final String LINE_SEPARATOR = System.getProperty("line.separator");//解決不同操作平臺寫入換行問題 public static void main(String[] args) throws IOException {FileWriter fileWriter = new FileWriter("abc.txt",true);//創建FileWriter對象,構造函數填寫目的地,true代表可以續寫,不覆蓋 fileWriter.write("456" + LINE_SEPARATOR + "789");//寫到緩存區中一個字符串 fileWriter.flush();//刷出緩沖區,寫到硬盤中 fileWriter.close();//關閉,關閉前會調用一次flush方法。因為寫的功能調用的是windows的系統資源,所以用完了要關了,但關了再寫就不能寫了 //想象一下記事本 //write就像寫了后沒保存,斷電后就沒了 //flush就相當于保存操作,把你寫到內存的東西保存到了硬盤 //close就相當于關閉了記事本,但是關閉時候提示了你保存(關閉前保存) } }執行完成后,看一下
寫完事了,在玩一下讀。讀的話用的哪個類,猜也能猜的差不多。FileReader
首先是一個字符一個字符讀,這個比較簡單,就是讀到了就返回,都不到就返回-1
public class FileReaderDemo {public static void main(String[] args) throws IOException {FileReader fileReader = new FileReader("abc.txt");//讀這個文件 int c;//用于接收單個字符 while((c = fileReader.read()) != -1)//如果當前讀到的字符不是-1,就一直讀。-1是規定的沒讀到的標識 {System.out.print((char)c);//讀到后轉一下顯示 }} }
一個一個讀不爽?一堆一堆讀!
這是我們要讀取的文件
這是讀的源碼
FileReader fileReader = new FileReader("abc.txt");//讀這個文件 char[] c = new char[3]; int len; //一次讀滿數組,如果讀到了,返回讀了幾個,如果沒讀到返回-1 while((len = fileReader.read(c)) != -1) {//輸出每次讀到的數組轉換成字符串 System.out.print(new String(c,0,len));//new String(char[] c,start,length)重新構造一個字符串,start作為開始索引,length是要獲取幾個 }看一下過程,首先我們聲明了一個數組c,利用fileReader.read(c)讀取。
第一次就把數組讀滿了,結果為123,返回的是讀到的個數,是3
第二次在讀的時候,讀到了456,也是讀滿了數組
第三次讀的時候,讀到了換行符,可以看到在windows下,換行符是\r\n(這不是重點。。)
以此類推還是依此類推?不管了,反正再往下讀,就是89J,最后一次讀到QK的時候,發現讀不滿數組了,讀到的長度為2,數組的前兩個位置被替換變成了QK,最后一個元素由于沒有讀到被替換,所以還是上回讀到的值
最后一次讀不到了,返回讀到的長度為-1,不進循環,程序結束!
讀寫都會了后,來個小practice試一試。拷貝D盤的abc.txt到E盤
public class CopyFileDemo {public static void main(String[] args) throws IOException {//拷貝文件的原理就是先讀在寫 FileReader fileReader = new FileReader("D:/abc.txt"); FileWriter fileWriter = new FileWriter("E:/abc.txt"); char[] c = new char[3]; int len; while((len = fileReader.read(c)) != -1){fileWriter.write(c,0,len);//每次讀幾個就寫幾個 }fileWriter.flush(); fileWriter.close(); fileReader.close(); } }--------------------------------------------------------------------------
緩沖區
緩沖區怎么理解呢?就比如你吃瓜子,毛嗑,向日葵,其實是一個東西。你撥一個吃一個肯定不爽,你有空就撥出來,然后放在一個盒子里面,想吃的時候直接拿,就非常海皮。在比如說,你去超市買東西,你推了一個購物車,買一個扔進去一個,到結賬的時候,你還得一個一個拿出來,不如在購物車里面加個框,最后把筐給它就完事了(學到沒有?)。計算機里面也是,讀一次寫一次效率肯定不高,可以先讀到一個緩沖區里面,然后最后緩沖區裝滿了,一次性把緩沖區的內容寫完,這樣效率就得到了提高。緩沖區的出現用于提高流的效率。
BufferedWriter
public static void main(String[] args) throws IOException {FileWriter fileWriter = new FileWriter("abc.txt"); BufferedWriter bw = new BufferedWriter(fileWriter);//緩沖區只是作為緩沖對象,真正的寫操作還是FileWriter來操作的 bw.write("123");//寫入數據到緩沖區 bw.flush();//刷到硬盤 bw.close();//bw關閉,底層關閉的是fileWriter }BufferedReader
這個方法有個readLine()方法,可以一次讀一行,如果讀不到就返回null,你也可以選擇一個一個讀,也是用read方法,只不過重寫了父類的read方法。為什么要重寫呢?這就需要分析一下原理了。
public static void main(String[] args) throws IOException {FileReader reader = new FileReader("abc.txt"); BufferedReader br = new BufferedReader(reader); String str = null; while((str = br.readLine()) != null){System.out.print(str); } }BufferedReader中read()方法實現原理
其實就好你去廚房吃饅頭,吃一個拿一個肯定不爽嘛,不如拿個筐去,裝在筐里面,想吃的時候直接從筐里取就很快。緩沖區也是,先拿到一些到內存中,用的時候直接從內存取就非常快了。如果不重寫這個方法,就會調用父類的read,而父類的read并不是從緩沖區里面讀的,效率比較低。重寫后拿到的數據都是緩沖區里面的數據。 原理就是先獲取一批數據放到一個數組里,然后調用一次read方法就從數組里面取一次,取沒了就再拿一次放到數組里,取到沒有為止。下面的代碼是模擬緩沖區read和readLine方法的實現。readLine就是用StringBuffer作為緩沖區,如果讀到了換行符就結束方法并返回一行的數據。 public class MyBufferedReader {FileReader fileReader; public MyBufferedReader(FileReader fileReader)構造函數傳遞需要被緩沖的對象 {this.fileReader = fileReader; }char[] cArr = new char[1024];//緩沖區 int count;//緩沖區里面元素的數量 int pos;//用于獲取緩沖區的角標 //自定義read方法 public int read() throws IOException {//筐里面沒有饅頭,去廚房取 if(count == 0) {count = fileReader.read(cArr);//如果緩沖區沒有了,就去拿 pos = 0;//重新獲取之后,要把角標重新設置為0 }//廚房也沒有饅頭了 //如果沒有數據,拿不到了,返回-1 if(count < 0){return -1; }//在筐里拿饅頭吃 char c = cArr[pos++];//每次自增變量獲取 count--;//緩沖區數量減少 return c; }//自定義readLine方法 public String readLine() throws IOException {StringBuffer sb = new StringBuffer(); //判斷/r/n int c; while((c = this.read()) != -1)//循環讀 {if(c == '\r')//如果是/r就跳過去,不追加到字符串中 {continue; }if(c == '\n')//如果是/n,就代表讀到了換行符,返回一行的字符串 {return sb.toString(); }sb.append(c);//日常追加字符串 }return sb.toString(); } } ------------------------------------------------------------------------------------- 不明白原理也沒關系,其實開發中也用不上,但最好了解一下把,不然面試的時候要問呢?字符流說完了,總結一下把,首先要了解字符流的兩個抽象父類,Writer和Reader,下面有一些子類,其中FileReader是讀文件用的(輸入),常用的方法有int read(),int read(char)。FileWriter是寫文件用的(輸出),常用的方法有writer(); flush(); close(); 還有給這兩位緩沖用的對象,BufferedWriter和BufferedReader,BufferWriter不用多說了,BufferWriter常用的方法有int read();和 String?readLine();這兩個方法都是重寫過的。還有一個LineNumberReader對象沒有說,就是讀到后加行號,一般用不上,非常簡單,自己看下就行了。
-----------------------------------------------------------------------------------------------------
字節流
接下來看字節流,字節流相比字符流就比較萬能了,但是對于處理文本來說還是要優先考慮字符流的。可以說字節流能處理任何類型的文件,比如.txt的,jgp的,mp3,mp4,mp5的,mp6有嘛?avi視頻文件等等。
InputStream -> FileInputStream,用字節流玩一玩文件,見名知意,寫文件用的。
public class FileInputStreamDemo {public static void main(String[] args) throws IOException {FileInputStream fis = new FileInputStream("abc.txt"); //一個一個讀 int i; while((i = fis.read()) != -1){System.out.print((char)i); }//一堆一堆讀 byte[] c = new byte[1024]; int len; while((len = fis.read(c)) != -1){System.out.print(new String(c,0,len)); }//讀小文件用,慎用 byte[] c2 = new byte[fis.available()];//fis.available文件字節數 int len2 = fis.read(c2); System.out.print(new String(c2,0,len2)); } }和字符流都差不多,只不過接受用的是byte字節,還多了一個fis.available()方法,大批量讀不建議使用。
讀完事了,來一個寫。用到了FileOutputStream
public class FileOutputStreamDemo {public static void main(String[] args) throws IOException {FileOutputStream fos = new FileOutputStream("abc.txt"); fos.write("123456".getBytes()); fos.flush();//字節流不需要調用flush方法,不需要先寫到緩沖區,直接和文件打交道。可以看到繼承了父類的flush,并沒有重寫, fos.close();//關閉還是要調用的,子類也進行了重寫 } }都是和字符流用法差不多,非常簡單。
來一個拷貝mp3的練習,順便用一下緩沖區,和字符流也差不多,不過多解釋。
----------------------------------------------------------------------------------------------------------------------------
轉換流
需求:讓用戶輸入一段話,按回車結束。如果不是over就輸出,是over結束程序。
如果不用轉換流,InputStream不存在字符流中的readLine()方法,所以自己寫要判斷換行符,非常麻煩。這時候就需要將字節流轉換成字符流解碼
InputStreamReader
InputStream is = System.in; InputStreamReader isr = new InputStreamReader(is);//將字節流轉換成字符流,放入一個字節流 BufferedReader br = new BufferedReader(isr);//緩沖區高效讀取 String line; while((line = br.readLine()) != null) {if(line.equals("over"))break; System.out.print(line.toString()); }OutputStreamWriter編碼
----------------------------------------------------------------------
學了這么多流對象?到底怎么實戰?
1.首先明確源和目的地
源:InputStream Reader
目的地:OutputStream Writer
2.明確數據是否是純文本
源
? ? |--是,Reader
? ?|--否,InputStream
目的地:
? ? |--是,Writer
? ? |--否,OutputStream
3.明確具體的設備
源設備:硬盤File,鍵盤System.in,內存Array,網絡Socket
目的地設備:硬盤File,控制臺System.out,內存Array,網絡Socket
4.是否需要額外功能
? ? 高效緩沖區,轉換流
-----------------------------------------------------------------------------------------------------------------
明確了這四點之后,來看幾個需求
1.復制一個文本文件
? ? 首先是純文本,選擇Reader和Writer,具體設備是硬盤,選擇FileReader和FileWriter,再看是否需要額外功能,需要緩沖區高效讀寫,那么選擇BufferedReader和BufferedWriter
2.讀取鍵盤錄入的數據寫到硬盤中
? ? 首先也是純文本,選擇Reader和Writer,具體設備是控制臺System.in和硬盤File,選擇InputStream is = System.in;
? ? 和FileWriter,需要額外功能嗎?考慮讀取到的數據需要轉換成字符會更好操作,所以選擇轉換流,字節轉換成字符選用
????InputStreamReader,需要高效嗎?需要!
????那就BufferedReader br = new BufferedReader(new InputStreamReader(System));
? ? BufferedWriter bw = new BufferedWriter(new FileWriter("abc.txt"));
3.將一個文本上的數據顯示在控制臺上
? ? 首先是純文本,選擇Reader和Writer,具體設備是硬盤和控制臺,選擇FileReader和OutputStream os = System.out;
? ? 需要額外功能嘛?需要轉換流,OutputStreamWriter(System.out),需要高效嘛?需要!
? ? BufferedReader br = new BufferedReader(new FileReader("abc.txt"));
? ? BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
4.將控制臺輸入的文本,輸出在控制臺上
? ? 純文本,Reader和Writer,具體設備控制臺,選擇InputStream is = System.in;和OutputStream os = System.out;
? ? 需要額外功能?轉換和高效
? ? BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
????BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
ok,只要明確了對象,使用的話就比較簡單了。
---------------------------------------------------------------------------------------------------
到這里,IO的基本對象就學的差不多了
字節流FileInputStream,FileOutputStream,BufferedInputStream,BufferedOutputStream
字符流FileReader,FileWriter,InputStreamReader,OutputStreamWriter,BufferedReader,BufferedWriter
但是這些對象都是操作的數據,操作不了屬性。比如獲取文件名稱,文件大小,操作文件夾等等。
這時候就出現了File類
File類將文件和文件夾封裝成了對象,更方便的操作文件和文件夾。沒有什么技術含量,就寫API的Demo了
//-----------------File構造的幾種方式--------------- File f1 = new File("abc.mp3"); File f2 = new File("c:\\","abc.txt"); File f = new File("c:\\"); File f3 = new File(f,"abc.txt"); File f4 = new File("c:" + File.separator + "abc.txt"); //----------------File常用獲取------------------ String name = f1.getName();//獲取文件名稱 String path = f1.getPath();//獲取相對路徑 String absPath = f1.getAbsolutePath();//獲取絕對路徑 long len = f1.length();//獲取長度 long time = f1.lastModified();//獲取最后修改時間 //看上去爽一點的 最后修改時間 Date date = new Date(time); DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); String timeStr = df.format(date); System.out.println(name); System.out.println(path); System.out.println(absPath); System.out.println(len); System.out.println(time); System.out.println(timeStr); //-----------------------創建與刪除-------------------------- //和輸出流不同的是,如果沒有就創建,有就不做操作 File f5 = new File("abc.txt"); boolean b = f5.createNewFile();//創建一個新的文件 File f6 = new File("abc"); boolean b2 = f6.mkdir();//創建文件夾 File f7 = new File("abc\\def"); boolean b3 = f7.mkdirs();//創建多個文件夾 File f8 = new File("abc\\def"); boolean b4 = f8.delete();//刪除,如果文件夾里面有內容,是不可以刪除的 f8.deleteOnExit();//在程序退出后刪除,比如IO流讀取完文件,把文件刪除 //----------------------------判斷-------------------------------- File f9 = new File("abc.txt"); boolean bb = f9.exists();//判斷文件或文件夾之前一定要判斷文件是否存在 boolean b5 = f9.isDirectory(); boolean b6 = f9.isFile(); //----------------------------替換--------------------------------- File f10 = new File("123.txt"); f10.renameTo(new File("456.txt")); //---------------------系統根目錄獲取容量---------------------------- File[] files = File.listRoots();//獲取所有盤符 for(File fx : files) {System.out.print(fx); }File fJ = new File("c:\\"); System.out.println(fJ.getFreeSpace());//可用 System.out.println(fJ.getTotalSpace());//總共 System.out.println(fJ.getUsableSpace());//已用 //--------------------------獲取目錄內容------------------------------------ File fQ = new File("c:\\"); String[] list = fQ.list(); for(String s : list) {System.out.println(s); }//--------------------------------過濾目錄內容----------------------------------- //找到所有.java的文件 File fK = new File("c;\\"); fK.list(new FilenameFilter() {@Override public boolean accept(File dir, String name) {return name.endsWith(".java"); } });深度遍歷文件夾 -- 遞歸思想
使用遞歸時,要注意兩點
1.結束條件
2.遞歸次數,小心內存溢出(光壓棧不彈棧)
public static void main(String[] args) {depthDir(new File("d:\\123"),0); }public static void depthDir(File f,int level) {System.out.println(printSpace(level) + f.getName()); level++; File[] files = f.listFiles(); for(int x = 0; x < files.length; x++){//如果當前是文件夾,就在進去遍歷 if(files[x].isDirectory()){depthDir(files[x],level); }else {System.out.println(printSpace(level)+ files[x].getName()); }} }private static String printSpace(int level) {StringBuffer sb = new StringBuffer(); for(int x = 0; x < level; x++){sb.append(" "); }return sb.toString(); }遍歷刪除文件夾,Windows下刪除文件只能從里往外刪
public static void main(String[] args) {File f = new File("d:\\123"); deleteDir(f); }private static void deleteDir(File f) {//遍歷,如果不是文件夾就刪除,是就繼續進去,等到進到最后一個了開始刪除 File[] files = f.listFiles(); for(int x = 0; x < files.length; x++){if(files[x].isDirectory()){deleteDir(files[x]); }else {files[x].delete(); }}f.delete(); }總結
以上是生活随笔為你收集整理的Java基础之IO流(持续更新中)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 动态gif图如何在线制作?gif动图制作
- 下一篇: 大鹏教你数据分析系列--运动员身材都是完