一、概述
1、介紹
I/O是 Input/Output 的縮寫,IO流用來處理裝置之間的資料傳輸,如讀/寫檔案,網路通訊等。Java對資料的操作是通過流的方式進行。java.io 包下提供了各種"流"類和介面,用以獲取不同種類的資料,並通過標準的方法輸入或輸出資料。
輸入input:讀取外部資料(磁碟、光碟等儲存裝置的資料)到程式(記憶體)中。
輸出output:將程式(記憶體)資料輸出到磁碟、光碟等儲存裝置中。
2、分類
按操作資料單位不同分為: 位元組流 (8 bit),字元流 (16 bit)。
按資料流的流向不同分為: 輸入流,輸出流。
按流的角色的不同分為: 節點流(檔案流),處理流。
節點流(檔案流):直接作用在檔案上,從資料來源或目的地讀寫資料。
處理流:不直接作用在檔案上,不直接連線到資料來源或目的地,而是"連線"在已存在的流(節點流或處理流)之上,通過對資料的處理為程式提供更為強大的讀寫功能。
節點流:
處理流:
3、IO流體系
四個頂層的抽象基類。
由這四個類派生出來的子類名稱都是以其父類作為子類名的字尾。例:InputStream的子類FileInputStream,Reader的子類FileReader。
IO流的體系如下:重點掌握高亮部分。
【訪問檔案】是節點流(檔案流),其他(除了抽象基類)都是處理流。
二、字元流
1、FileReader(輸入)
用來讀取字元檔案的便捷類。此類的構造方法假定預設字元編碼和預設位元組緩衝區大小都是適當的。要自己指定這些值,可以先在FileInputStream上構造一個InputStream。
read():一次讀一個字元,指標會指向下一個字元。讀到末尾返回-1。
程式碼示例:讀檔案
1 // 檔案:F:\\hello.txt 2 // 內容:helloworld123中國人1 3 public class Main { 4 public static void main(String[] args) { 5 try (FileReader fr = new FileReader(new File("F:\\hello.txt"));) { 6 int data; 7 while ((data = fr.read()) != -1) { 8 System.out.print((char) data); 9 } 10 } catch (Exception e) { 11 } 12 } 13 } 14 15 // 結果 16 helloworld123中國人的
read(char[] buff):一次讀 buff.length 個字元,返回讀取的個數。讀到末尾返回-1。
程式碼示例:錯誤的寫法
1 // 檔案:F:\\hello.txt 2 // 內容:helloworld123中國人1 3 public class Main { 4 public static void main(String[] args) { 5 try (FileReader fr = new FileReader(new File("F:\\hello.txt"));) { 6 char[] buff = new char[5]; 7 int len; 8 9 while ((len = fr.read(buff)) != -1) { 10 // 方式一: 11 for (char c : buff) { 12 System.out.print(c); 13 } 14 15 // 方式二: 16 // String str = new String(buff); 17 // System.out.print(str); 18 } 19 } catch (Exception e) { 20 } 21 } 22 } 23 24 // 結果.方式一 和 方式二都是 25 helloworld123中國人13中國
程式碼示例:正確的寫法
1 // 檔案:F:\\hello.txt 2 // 內容:helloworld123中國人1 3 public class Main { 4 public static void main(String[] args) { 5 try (FileReader fr = new FileReader(new File("F:\\hello.txt"));) { 6 char[] buff = new char[5]; 7 int len; 8 9 while ((len = fr.read(buff)) != -1) { 10 // 方式一: 11 for (int i = 0; i < len; i++) { 12 System.out.print(buff[i]); 13 } 14 15 // 方式二:從 buff 的下標 0 開始取字元,取 len 個 16 // String str = new String(buff, 0, len); 17 // System.out.print(str); 18 } 19 } catch (Exception e) { 20 } 21 } 22 } 23 24 // 結果 25 helloworld123中國人1
深刻理解 read(char[] buff) 方法:一次讀進 buff.length 個字元,列印。讀取下次的時候,上次讀取的字元其實還在字元陣列中,所以最後"人1"只覆蓋了上一次的前兩個字元,使得最後字元陣列裡是"人 1 3 中 國"。
2、FileWriter(輸出)
構造方法必須明確被操作的檔案,若指定目錄下不存在,會被建立;若存在,會被覆蓋。相關API如下:
new FileWriter(String fileName):在指定目錄下建立
new FileWirter(String fileName, boolean append):是否為追加資料
void write(String int):過載,將字串寫入到流中
void write(char[] buff):寫入字元陣列
void wirte(char[] buff, int off, int len):寫入字元陣列的某一部分
void flush():重新整理流,流可以繼續使用
void close():先重新整理流,關閉流,流不可以繼續使用
注:通過write()寫入換行用 \r\n,對應的位元組為 13 10
程式碼示例:寫檔案
1 public class Main { 2 public static void main(String[] args) { 3 // 預設不追加,覆蓋原有的檔案內容 4 try (FileWriter fw = new FileWriter(new File("F:\\hello.txt"));) { 5 6 // 表示在原有檔案內容的基礎上追加 7 // FileWriter fw = new FileWriter(new File("F:\\hello.txt"), true); 8 fw.write("我有a dream!\n"); 9 fw.write("you need to have a dream!"); 10 11 // fw.flush(); 12 } catch (Exception e) { 13 } 14 } 15 }
3、複製檔案
程式碼示例:字元流複製文字檔案
1 public class Main { 2 public static void main(String[] args) { 3 try (FileReader fr = new FileReader(new File("F:\\hello.txt")); 4 FileWriter fw = new FileWriter(new File("F:\\hello_copy.txt"));) { 5 6 char[] buff = new char[5]; 7 int len; 8 9 // 一次讀出len個字元到buff. 10 while ((len = fr.read(buff)) != -1) { 11 fw.write(buff, 0, len); 12 fw.flush(); 13 } 14 } catch (Exception e) { 15 } 16 } 17 }
注:不能使用字元流來處理圖片,視訊等位元組資料。
三、位元組流
1、FileInputStream(輸入)
基本用法和字元流一樣。
程式碼示例:讀檔案,可能出現亂碼。
1 // 檔案:F:\\hello.txt 2 // 內容:helloworld123中國人1 3 public class Main { 4 public static void main(String[] args) { 5 try (FileInputStream fis = new FileInputStream(new File("F:\\hello.txt"));) { 6 // fis.available():返回檔案位元組數 7 byte[] buffer = new byte[5]; 8 int len; 9 10 // 按位元組來讀取 11 while ((len = fis.read(buffer)) != -1) { 12 String str = new String(buffer, 0, len); 13 System.out.print(str); 14 } 15 } catch (Exception e) { 16 17 } 18 } 19 } 20 21 // 結果,有亂碼.原因就是一個漢字佔兩位元組,被分割了. 22 helloworld123��國���1
2、FileOutputStream(輸出)
基本用法和字元流一樣,不同於在寫入時不需要flush()。
void write(int b):一次寫入一個位元組
void write(byte[] b):寫入一個位元組陣列
void write(byte[] b, int off, int len):寫入一個位元組陣列,off開始,len位元組數
程式碼示例:寫檔案
1 public class Main { 2 public static void main(String[] args) { 3 try (FileOutputStream fos = new FileOutputStream(new File("F:\\hello.txt"));) { 4 5 fos.write("我有a dream!\n".getBytes()); 6 fos.write("you need to have a dream!".getBytes()); 7 8 } catch (Exception e) { 9 } 10 } 11 } 12 13 // 結果,有亂碼. 14 鎴戞湁a dream! 15 you need to have a dream!
總結:因為一個漢字佔兩個位元組。所以,
字元流,適用於讀寫文字檔案。不適用於位元組流,容易出現亂碼。
位元組流,適用於讀寫二進位制檔案,比如圖片,音訊,視訊等。
3、複製檔案
程式碼示例:用位元組流處理非文字檔案(圖片,視訊等)。位元組流複製圖片。
1 // 檔案:F:\\hello.PNG 2 public class Main { 3 public static void main(String[] args) { 4 try (FileInputStream fis = new FileInputStream(new File("F:\\hello.PNG")); 5 FileOutputStream fos = new FileOutputStream(new File("F:\\hello_1.PNG"));) { 6 7 byte[] buffer = new byte[5]; 8 int len; 9 10 // 一次性讀一個 len 個位元組到buffer. len <= buffer 11 while ((len = fis.read(buffer)) != -1) { 12 fos.write(buffer, 0, len); 13 } 14 } catch (Exception e) { 15 } 16 } 17 } 18 19 // 此程式碼用於複製 3.64G 的檔案花費 56.574s