Java基礎(二十三)——IO流
IO概述
什麼是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
。把以指定是否需要追加續寫換行。
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 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();
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();
續寫和換行
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 }