1. 程式人生 > 實用技巧 >Java基礎(二十三)——IO流

Java基礎(二十三)——IO流

IO概述

什麼是IO?

  Java中I/O的操作主要是靠java.io包下面的類和介面來實現的,進入輸入、輸出操作。輸入也可以叫做讀取資料,輸出也可以叫做寫入資料

IO分類

根據資料的流向分為:輸入流和輸出流

  • 輸入流:把資料從其他裝置上讀取到記憶體當中的流。

  • 輸出流:把資料從記憶體當中寫入到其他裝置上的流。

根據資料的型別分為:位元組流和字元流

  • 位元組流:以位元組為單位,讀寫資料的流

  • 字元流:以字元為單位,讀寫資料的流。

IO流的頂級父類

輸入流輸出流
位元組流 位元組輸入流InputStream 位元組輸出流OutputStream
字元流 字元輸入流Reader
字元輸出流Writer

位元組流

一切皆為位元組

  一切檔案資料(文字文件,圖片,視訊等)在儲存時,都是以二進位制數字的形式儲存的,都是一個一個的位元組,那麼資料在進行傳輸的時候也是如此。所以位元組流可以傳輸任意檔案的資料。在操作流的時候,我們要明確,無論使用了什麼樣的流物件,底層傳輸的始終為二進位制資料。

位元組輸出流[OutputStream]

  java.io.OutputStream此抽象類是表示輸出位元組流的所有類的超類,將指定的位元組資訊寫入到目的地。它定義了位元組輸出流的基本共性的api方法:

  • public void close():關閉此輸出流並釋放與此流相關的其他任何系統資源。

  • public void flush():重新整理此輸出流並強制任何緩衝的的輸出位元組資訊被寫入。

  • public void write(byte[] b):將b.length位元組從指定的位元組陣列寫入到此輸出流中。

  • public void write(byte[] b,int off,int len):從指定的位元組陣列寫入len個位元組,從偏移量off開始輸出到此輸出流中

  • public abstract void write(int b):將指定的位元組輸出到此輸出流中。

備註:close方法,當完成流的操作時,必須呼叫此方法,釋放系統資源。

FileOutputStream類

  java.io.FileOutputStream類是檔案位元組輸出流,用於將資料寫入到檔案中。

構造方法

  • public FileOutputStream(File file):建立一個向指定 File 物件表示的檔案中寫入資料的檔案輸出流。

  • public FileOutputStream(String name):建立檔案輸出流以指定的的檔名稱寫入檔案中。

  當你建立一個流物件時,必須先傳遞一個檔案路徑,該路徑下,如果沒有這個檔案,會建立該檔案,如果有這個檔案,會清空這個檔案當中的資料。

示例程式碼:

1         //  1.建立一個FileOutputStream類物件,構造方法中傳遞寫入資料的目的地。
2         FileOutputStream fos = new FileOutputStream("day28_IO\\a.txt");
3         //  2.呼叫FileOutputStream物件中的方法write,把資料寫入到檔案中
4         fos.write(97);
5         //  3.釋放資源。
6         fos.close();    

原理解析:

資料的追加續寫

如何在保留目標檔案中的資料,還能繼續新增新的資料到目標檔案中?

  • public FileOutputStream(File file,boolean append):建立檔案輸出流以寫入由指定的File物件表示的檔案中。

  • public FileOutputStream(String name,boolean append):建立檔案輸出流以指定的名稱寫入檔案中。

這兩個構造方法,引數中都需要傳入一個boolean型別的值,true表示的追加資料,false表示的清空原有資料。在這樣的情況下建立輸出流物件,就可以指定是否需要在檔案的末尾追加。

程式碼演示:

1         //1.建立物件
2         FileOutputStream fos = new FileOutputStream("day28_IO\\c.txt", true);
3         //2.呼叫write()
4         fos.write("HelloWorld".getBytes());
5         //3.關閉流物件
6         fos.close();

寫入換行

Windows系統裡,換行符號是\r\n。把以指定是否需要追加續寫換行。

Linux系統裡,換行符號是 /n

mac系統裡,換行符號是/r

Unix系統裡,每行結尾只有換行,即/n

回車符\r和換行符\n

  • 回車符:回到一行的開頭

  • 換行符:下一行(newLine)。

系統中的換行:

  • Windows系統中,每行結尾是回車+換行。即\r\n

  • Unix系統中,每行的結尾只有換行,即/n

  • Mac系統中,每行的結尾是回車,即/r

程式碼示例:

 1 public static void main(String[] args) throws IOException {
 2         //1.建立物件
 3         FileOutputStream fos = new FileOutputStream("day28_IO\\c.txt", true);
 4         // 實現資料換行
 5         fos.write("\r\n".getBytes());
 6         //2.呼叫write()
 7         fos.write("Java31".getBytes());
 8         //3.關閉流物件
 9         fos.close();
10     }

位元組輸入流【InputStream】

java.io.InputStream此抽象類是表示位元組輸入流的所有類的超類。可以讀取位元組資訊到記憶體中。他定義了位元組輸入流的基本共性的api方法:

  • public void close():關閉此輸入流並釋放與此流相關的其他的任何系統資源。

  • public abstract int read():從輸入流中讀取資料的下一個位元組。

  • public int read(byte[] b):從輸入流中讀取一些位元組數,並將它們儲存到位元組陣列b當中。

備註:close方法,當完成流的相關操作後,需要呼叫此方法關閉輸入流,釋放系統資源。

FileInputStream類

java.io.FileInputStream類是檔案輸入流,從檔案中讀取位元組。

構造方法
  • FileInputStream(File file):通過開啟與實際檔案的連線來建立一個FileInputStream,該檔案由檔案系統中的File物件file命名。

  • FileInputStream(String name):通過開啟與實際檔案的連線來建立一個FileInputStream,該檔案由檔案系統中的路徑名name命名。

    當你建立一個流物件時,必須傳入一個檔案路徑。該路徑下,如果沒有該檔案,會丟擲FileNotFoundException。

程式碼示例:

1  // 1. 建立一個FileInputStream類物件,構造方法中繫結要讀取的檔案
2 FileInputStream fis = new FileInputStream("day28_IO\\c.txt");
3 // 2. 使用FileInputStream類物件中的方法read(),讀取檔案中的資料
4  int len = 0 ;// 表示記錄讀取到的位元組
5     while ( (len = fis.read()) != -1) {
6         System.out.print((char)len+"");// Hello
7     }
8 // 3. 釋放資源,關閉流物件。
9 fis.close();

  可以使用位元組陣列來讀取資料:read(byte[] b):從輸入流中讀取多個位元組,並且將其儲存到緩衝區陣列b當中。當讀取到檔案的末尾時,返回一個-1

程式碼演示:

 1   //1.建立物件
 2   FileInputStream fis = new FileInputStream("day28_IO\\c.txt");
 3   int  len = 0;// 記錄讀取到的有效位元組個數
 4   byte[] bytes = new byte[1024];// 2^10 1024b = 1kb
 5   while ((len = fis.read(bytes)) != -1) {
 6      // byte[] 轉換成String
 7      // String(byte[] bytes,int offset,int length) 把位元組陣列的一部分轉換成字串
 8      //System.out.println(Arrays.toString(bytes));//[B@74a14482
 9      System.out.println(new String(bytes,0,len));
10  }

備註:使用陣列讀取,每次可以讀取多個位元組,減少了系統間的IO操作次數,從而提高了讀取的效率,建議使用。

練習:通過位元組流實現圖片複製 FileInputStream 和 FileOutputStream

實現從桌面的G1.jpeg圖片複製到E:\documents\day28_IO目錄下。

原理:從已知的檔案中讀取位元組,再把讀取到的位元組寫入到另一個檔案中。

 1 public static void main(String[] args) throws IOException {
 2         // 構建開始時間
 3         long start = System.currentTimeMillis();
 4         // 1.建立一個位元組輸入流物件,構造方法中繫結需要讀取的圖片路徑
 5         FileInputStream fis = new FileInputStream("C:\\Users\\admin\\Desktop\\3.gif");
 6         // 2.建立一個位元組輸出流物件,構造方法中繫結需要寫入的檔案
 7         FileOutputStream fos = new FileOutputStream("E:\\documents\\day28_IO\\3.gif");
 8        byte[] bytes = new byte[1024];
 9         int len = 0;//記錄讀取到的有效位元組個數
10         while ((len = fis.read(bytes)) != -1) {
11             //4.使用位元組輸出流物件中的write方法,把讀取到位元組寫入到指定的檔案中
12             fos.write(bytes,0,len);
13         }
14         //  5.釋放資源。(先開的後關,後開的先關)
15         fos.close();
16         fis.close();
17         // 構建結束的時間
18         long end = System.currentTimeMillis();
19         System.out.println("複製檔案耗費的時間為:" + (end - start) + "ms");
20 }

備註:流的關閉順序:先開後關,後開先關。

字元流

當使用位元組流讀取檔案檔案的時候,可能會引發一點小問題。如果你遇到了中文字元時,可能不會顯示完整的字元。那就是因為一箇中文字元可能佔用多個位元組儲存。所以Java提供了一些字元流類,以字元為單位讀寫資料,專門用於處理文字文件檔案。

字元輸入流【Reader】

java.io.Reader抽象類是表示用於讀取字元流的所有類的超類,可以讀取字元資訊到記憶體當中,它定義了字元輸入流的基本共性的api方法:

  • public void close():關閉此輸入流並且釋放與此流相關的其他系統資源。

  • public int read():從輸入流中讀取一個字元。

  • public int read(char[] chuf):從輸入流中一次讀取多個字元,並將它們儲存到字元陣列chuf當中。

FileReader類

java.io.FileReader類主要是用於讀取字元檔案的便捷類。構造方法使用時預設的編碼字符集和預設的位元組緩衝區。

備註:

1. 字元編碼:位元組與字元的對應規則。Windows系統中的中文編碼預設是GBK編碼表,idea中採用UTF-8
2. 位元組緩衝區:一個位元組陣列,用來臨時儲存位元組資料。

構造方法

  • FileReader(File file):建立一個新的FileReader物件,指定需要讀取的file物件。

  • FileReader(String filename):建立一個新的FileReader物件,指定需要讀取的檔名稱。

當你建立一個流物件時,必須傳入一個檔案路徑。類似於FileInputStream。

程式碼示例。

 1 // 1.建立物件,構造方法中繫結需要讀取的檔案資料來源
 2 FileReader fr = new FileReader("day28_IO\\c.txt");
 3 // 2.使用該物件中的方法read()來讀取檔案資料
 4 // int read() 讀取單個字元並返回
 5 /*int len = fr.read();
 6         System.out.println((char)len);// 72 H  張*/
 7 int len = 0;// 記錄讀取的字元 
 8 while ((len = fr.read()) != -1) {
 9     System.out.print((char)len);
10 }

  使用字元陣列讀取資料:read(char[] chuf),每次讀取chuf的長度個字元到陣列當中,返回讀取到有效字元的個數。

  當它讀取到末尾時,返回-1

程式碼示例:

 1 // 1.建立物件,構造方法中繫結需要讀取的檔案資料來源
 2 FileReader fr = new FileReader("day28_IO\\c.txt");
 3 char[] chuf = new char[1024];
 4 int len = 0;// 記錄的是每次讀取的有效的字元個數
 5 //2. 使用該物件中的方法read()來讀取檔案資料
 6 while ((len = fr.read(chuf)) != -1) {
 7     // 字元陣列轉換成字串  
 8     System.out.println(new String(chuf,0,len));
 9 }
10 // 3.釋放資源,關閉此輸入流。
11 fr.close();

字元輸出流【Writer】

java.io.Writer抽象類是表示用於輸出字元流的所有類的超類。將指定的字元資訊寫入到目的地中。它定義了字元輸出流的基本共性的api方法:

  • void write(int c):寫入單個字元。

  • void write(char[] chuf):寫入字元陣列。

  • abstract void write(char[] chuf,int off,int len):寫入char陣列的一部分,從char陣列的起始索引值off開始,len個寫入字元個數。

  • void write(String str):寫入字串。

  • void write(String str,int off,in len):寫入字串的一部分,從字串的起始索引off開始,寫入len個字元個數。

  • void flush():重新整理該流的緩衝。

  • void close():關閉此流,但是需要先重新整理它。

FileWriter類

java.io.FileWriter類是用於寫入字元到檔案中。構造方法使用系統預設的字元編碼和預設的位元組緩衝區。

構造方法

  • FileWriter(File file):建立一個新的FileWriter,指定寫入的file物件。

  • FileWriter(String filename):建立一個新的FileWriter,指定需要寫入的檔案的名稱。

當你建立一個流物件時,必須傳入一個檔案路徑,類似於FileOutputStream。

程式碼示例:

 1 public static void main(String[] args) throws IOException {
 2         // 1.建立一個FileWriter物件,構造方法中繫結需要寫入資料的目的地。
 3         FileWriter fw = new FileWriter("day28_IO\\e.txt");
 4         // 2.使用fileWriter物件中的方法write,把資料寫入到記憶體緩衝區中(字元轉換為位元組的過程)
 5         // void write(int c) 寫入單個字元
 6         fw.write(122);// K  z
 7         // 3.使用FileWriter物件的中的方法flush,把記憶體緩衝區中的資料重新整理到檔案中。
 8         //fw.flush();
 9         // 4.釋放資源(會先把記憶體緩衝區中的資料重新整理到檔案中)
10         fw.close();
11  }

關閉和重新整理

因為內建緩衝區的原因,如果不關閉輸出流,無法寫入字元到檔案中。但是關閉流物件,是無法繼續寫入資料到檔案中。如果既想寫入資料到檔案中,又想繼續使用流物件,那麼就需要flush方法。

  • flush:重新整理緩衝區,流物件可以繼續使用

  • close:先重新整理緩衝區,然後會通知系統釋放資源,關閉流物件,流物件不可以再使用。

程式碼演示:

 1  public static void main(String[] args) throws IOException {
 2         // 1.建立物件
 3         FileWriter fw = new FileWriter("day28_IO\\e.txt");
 4         // 2.呼叫write方法
 5         fw.write(97);// a
 6         // 3. 重新整理
 7         fw.flush();
 8         // 重新整理之後流可以繼續使用
 9         fw.write(98);
10         // 4. 關閉流物件
11         fw.close(); 
12        // close方法之後,流已經關閉了,已經從記憶體當中消失了,流不能再使用了,如果使用丟擲Stream closed 
13         fw.write(99);
14     }

  寫入字元陣列:write(char[] chuf)write(char[] chuf,int off,int len),每次可以寫入一個字元陣列的資料,用法類似於FileOutputStream

程式碼示例:

 1 //1. new
 2         FileWriter fw = new FileWriter("day28_IO\\f.txt");
 3         //2. write
 4         //2.1 char[]
 5         char[] chars = new char[]{'a','b','c','d','e','f'};
 6         fw.write(chars);// abcdef
 7         //abstract void write(char[] chuf,int off,in len):寫入字元陣列的一部分,從off起始索引開始,寫入len個字元個數
 8         fw.write(chars, 3, 3);// def
 9         //void write(String str):寫入一個字串
10         fw.write("你好,Java");
11         //void write(String str,int off,int len):寫入字串的一部分,從off起始索引開始,寫入len個字元個數
12         fw.write("你好,Java",3,4);// Java
13         //3. flush
14         //fw.flush();
15         //4. 關閉
16         fw.close();

續寫和換行:操作類似於FileOutputStream

 1  public static void main(String[] args) throws IOException {
 2         // 1. new
 3         FileWriter fw = new FileWriter("day28_IO\\g.txt", true);
 4         //2. write
 5         char[] chars = new char[50];
 6         //fw.write();
 7         for (int i = 0; i < 50; i++) {
 8             chars[i] = (char) (20000+i);
 9             fw.write(chars[i] + "\r\n");//換行性質
10         }
11         //3. 重新整理
12         fw.flush();
13         //4. 關閉
14         fw.close();
15     }