1.位元組流 1.1 位元組輸入流【inputStream】 java.io.InputStream 抽象類是表示位元組輸入流的所有類的超類,可以讀取位元組信息到記憶體中。 它定義了位元組輸入流的基本共性功能方法。 public void close() :關閉此輸入流並釋放與此流相關聯的任何系統資源。 pub ...
1.位元組流
1.1 位元組輸入流【inputStream】
java.io.InputStream 抽象類是表示位元組輸入流的所有類的超類,可以讀取位元組信息到記憶體中。 它定義了位元組輸入流的基本共性功能方法。
- public void close() :關閉此輸入流並釋放與此流相關聯的任何系統資源。
- public abstract int read() : 從輸入流讀取數據的下一個位元組。
- public int read(byte[] b) : 從輸入流中讀取一些位元組數,並將它們存儲到位元組數組 b中 。
小貼士:
close方法,當完成流的操作時,必須調用此方法,釋放系統資源。
1.2 FileInputStream
java.io.FileInputStream 類是文件輸入流,從文件中讀取位元組。
構造方法:
-
- FileInputStream(File file) : 通過打開與實際文件的連接來創建一個 FileInputStream ,該文件由文件系統中的 File對象 file命名。
-
FileInputStream(String name) : 通過打開與實際文件的連接來創建一個 FileInputStream ,該文件由文件系統中的路徑名 name命名。
當你創建一個流對象時,必須傳入一個文件路徑。該路徑下,如果沒有該文件,會拋出 FileNotFoundException 。
示例代碼:
1 public class FileInputStreamConstructor throws IOException{ 2 public static void main(String[] args) { 3 // 使用File對象創建流對象 4 File file = new File("a.txt"); 5 FileInputStream fos = new FileInputStream(file); 6 // 使用文件名稱創建流對象 7 FileInputStream fos = new FileInputStream("b.txt"); 8 } 9 }
讀取位元組數據:
1. 讀取位元組: read 方法,每次可以讀取一個位元組的數據,轉化為int類型的數據,當讀取到文件末尾,則返回 -1 ,代碼使用演示:
1 public class FISRead { 2 public static void main(String[] args) throws IOException{ 3 // 使用文件名稱創建流對象 4 FileInputStream fis = new FileInputStream("read.txt"); 5 // 讀取數據,返回一個位元組 6 int read = fis.read(); 7 System.out.println((char) read); 8 read = fis.read(); 9 System.out.println((char) read); 10 read = fis.read(); 11 System.out.println((char) read); 12 read = fis.read(); 13 System.out.println((char) read); 14 read = fis.read(); 15 System.out.println((char) read); 16 // 讀取到末尾,返回-1 17 read = fis.read(); 18 System.out.println( read); 19 // 關閉資源 20 fis.close(); 21 } 22 }
結果如下:
改進讀取方式,使用迴圈讀取的方式。代碼使用演示:
1 public class FISRead { 2 public static void main(String[] args) throws IOException{ 3 // 使用文件名稱創建流對象 4 FileInputStream fis = new FileInputStream("read.txt"); 5 // 定義變數,保存數據 6 int b ; 7 // 迴圈讀取 8 while ((b = fis.read())!=-1) { 9 System.out.println((char)b); 10 } 11 // 關閉資源 12 fis.close(); 13 } 14 }
結果如下:
小貼士:
1.每次讀取了一個位元組,會自動轉化為int類型的數據。2. 流操作完畢後,必須釋放系統資源,調用close方法,千萬記得。
2. 使用位元組數組讀取: read(byte[] b) ,每次讀取b的長度個位元組到數組中,返回讀取到的有效位元組個數,讀取到數據末尾時,返回 -1 ,代碼使用演示:
1 public class FISRead { 2 public static void main(String[] args) throws IOException{ 3 // 使用文件名稱創建流對象. 4 FileInputStream fis = new FileInputStream("read.txt"); // 文件中為 abcde 5 // 定義變數,作為有效個數 6 int len ; 7 // 定義位元組數組,作為裝位元組數據的容器 8 byte[] b = new byte[2]; 9 // 迴圈讀取 10 while (( len= fis.read(b))!=-1) { 11 // 每次讀取後,把數組變成字元串列印 12 System.out.println(new String(b)); 13 } 14 // 關閉資源 15 fis.close(); 16 } 17 }
結果如下:
錯誤數據 ed ,是由於後一次讀取時,只讀取一個位元組 e ,數組中,上次讀取的數據沒有被完全替換,所以要通過 len ,獲取有效的位元組長度,代碼使用演示:
1 public class FISRead { 2 public static void main(String[] args) throws IOException{ 3 // 使用文件名稱創建流對象. 4 FileInputStream fis = new FileInputStream("read.txt"); // 文件中為 abcde 5 // 定義變數,作為有效個數 6 int len ; 7 // 定義位元組數組,作為裝位元組數據的容器 8 byte[] b = new byte[2]; 9 // 迴圈讀取 10 while (( len= fis.read(b))!=-1) { 11 // 每次讀取後,把數組的有效位元組部分,變成字元串列印 12 System.out.println(new String(b,0,len));// len 每次讀取的有效位元組個數 13 } 14 // 關閉資源 15 fis.close(); 16 } 17 }
小貼士:
使用數組讀取,每次可讀取多個位元組,減少了系統間的IO操作次數,從而提高了讀寫的效率,開發中建議優先使用數組讀取。
1.3 位元組流:圖片複製
複製圖片原理圖解:
代碼如下:
1 public class Copy { 2 public static void main(String[] args) throws IOException { 3 // 1.創建流對象 4 // 1.1 指定數據源 5 FileInputStream fis = new FileInputStream("D:\\test.jpg"); 6 // 1.2 指定目的地 7 FileOutputStream fos = new FileOutputStream("test_copy.jpg"); 8 9 // 2.讀寫數據 10 // 2.1 定義數組 11 byte[] b = new byte[1024]; 12 // 2.2 定義長度 13 int len; 14 // 2.3 迴圈讀取 15 while ((len = fis.read(b))!=-1) { 16 // 2.4 寫出數據 17 fos.write(b, 0 , len); 18 } 19 20 // 3.關閉資源 21 fos.close(); 22 fis.close(); 23 } 24 }
小貼士:
流的關閉原則:先開後關,後開先關。
2.字元流
當使用位元組流讀取文本文件時,可能會有一個小問題。就是遇到中文字元時,可能不會顯示完整的字元,那是因為一個中文字元可能占用多個位元組存儲。所以Java提供一些字元流類,以字元為單位讀寫數據,專門用於處理文本文件。
2.1 字元輸入流【Reader】
java.io.Reader 抽象類是表示用於讀取字元流的所有類的超類,可以讀取字元信息到記憶體中。它定義了字元輸入流的基本共性功能方法。
- public void close() :關閉此流並釋放與此流相關聯的任何系統資源。
- public int read() : 從輸入流讀取一個字元。
- public int read(char[] cbuf) : 從輸入流中讀取一些字元,並將它們存儲到字元數組 cbuf中 。
2.2 FileReader
java.io.FileReader 類是讀取字元文件的便利類。構造時使用系統預設的字元編碼和預設位元組緩衝區。
小貼士:
1. 字元編碼:位元組與字元的對應規則。Windows系統的中文編碼預設是GBK編碼表。idea中為預設為UTF-82. 位元組緩衝區:一個位元組數組,用來臨時存儲位元組數據。
構造方法:
- FileReader(File file) : 創建一個新的 FileReader ,給定要讀取的File對象。
-
FileReader(String fileName) : 創建一個新的 FileReader ,給定要讀取的文件的名稱。
當你創建一個流對象時,必須傳入一個文件路徑。類似於FileInputStream 。
構造舉例,代碼如下:
1 public class FileReaderConstructor throws IOException{ 2 public static void main(String[] args) { 3 // 使用File對象創建流對象 4 File file = new File("a.txt"); 5 FileReader fr = new FileReader(file); 6 // 使用文件名稱創建流對象 7 FileReader fr = new FileReader("b.txt"); 8 } 9 }
讀取字元數據:
讀取字元: read 方法,每次可以讀取一個字元的數據,轉化為int類型,讀取到文件末尾時,返回 -1 ,迴圈讀取,代碼使用演示:
1 public class FRRead { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileReader fr = new FileReader("read.txt"); 5 // 定義變數,保存數據 6 int b ; 7 // 迴圈讀取 8 while ((b = fr.read())!=-1) { 9 System.out.println((char)b); 10 } 11 // 關閉資源 12 fr.close(); 13 } 14 }
結果:
小貼士:每次讀取了一個字元,會自動轉化為int類型。
1 public class FRRead { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileReader fr = new FileReader("read.txt"); 5 // 定義變數,保存有效字元個數 6 int len ; 7 // 定義字元數組,作為裝字元數據的容器 8 char[] cbuf = new char[3]; 9 // 迴圈讀取 10 while ((len = fr.read(cbuf))!=-1) { 11 System.out.println(new String(cbuf)); 12 } 13 // 關閉資源 14 fr.close(); 15 } 16 }
獲取有效的字元改進,代碼使用演示:
1 public class FISRead { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileReader fr = new FileReader("read.txt"); 5 // 定義變數,保存有效字元個數 6 int len ; 7 // 定義字元數組,作為裝字元數據的容器 8 char[] cbuf = new char[2]; 9 // 迴圈讀取 10 while ((len = fr.read(cbuf))!=-1) { 11 System.out.println(new String(cbuf,0,len)); 12 } 13 // 關閉資源 14 fr.close(); 15 } 16 }
結果:
2.3 字元輸出流【Writer】
-
-
public abstract void flush()
:刷新此輸出流並強制寫出任何緩衝的輸出字元。 -
public void write(int c)
:寫出一個字元。 -
public void write(char[] cbuf)
:從指定的字元數組中寫出字元到此輸出流。 -
public abstract void write(char[] b, int off, int len)
:從指定的字元數組寫出從偏移量 off開始, len長度的字元,輸出到此輸出流。 -
public void write(String str)
:寫出一個字元串。
2.4 FileWriter
-
-
FileWriter(String fileName)
: 創建一個新的 FileWriter,給定要寫出的文件的名稱。
當你創建一個流對象時,必須傳入一個文件路徑,類似於FileOutputStream。
代碼如下:
1 public class FileWriterConstructor { 2 public static void main(String[] args) throws IOException { 3 // 使用File對象創建流對象 4 File file = new File("a.txt"); 5 FileWriter fw = new FileWriter(file); 6 7 // 使用文件名稱創建流對象 8 FileWriter fw = new FileWriter("b.txt"); 9 } 10 }
基本寫出數據:
寫出字元:write(int b)
方法,每次可以寫出一個字元數據,代碼使用演示:
代碼如下:
1 public class FWWrite { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileWriter fw = new FileWriter("fw.txt"); 5 // 寫出數據 6 fw.write(97); // 寫出第1個字元 7 fw.write('b'); // 寫出第2個字元 8 fw.write('C'); // 寫出第3個字元 9 fw.write(30000); // 寫出第4個字元,中文編碼表中30000對應一個漢字。 10 11 /* 12 【註意】關閉資源時,與FileOutputStream不同。 13 如果不關閉,數據只是保存到緩衝區,並未保存到文件。 14 */ 15 // fw.close(); 16 } 17 }
結果:
小貼士:
1. 未調用close方法,數據只是保存到了緩衝區,並未寫出到文件中。
關閉和刷新
因為內置緩衝區的原因,如果不關閉輸出流,無法寫出字元到文件中。但是關閉的流對象,是無法繼續寫出數據的。如果我們既想寫出數據,又想繼續使用流,就需要flush
方法了。
-
-
close
:關閉流,釋放系統資源。關閉前會刷新緩衝區。
代碼使用演示:
1 public class FWWrite { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileWriter fw = new FileWriter("fw.txt"); 5 // 寫出數據,通過flush 6 fw.write('刷'); // 寫出第1個字元 7 fw.flush(); 8 fw.write('新'); // 繼續寫出第2個字元,寫出成功 9 fw.flush(); 10 11 // 寫出數據,通過close 12 fw.write('關'); // 寫出第1個字元 13 fw.close(); 14 fw.write('閉'); // 繼續寫出第2個字元,【報錯】java.io.IOException: Stream closed 15 fw.close(); 16 } 17 }
小貼士:
即便是flush方法寫出了數據,操作的最後還是要調用close方法,釋放系統資源。
寫出其他數據
寫出字元數組 :write(char[] cbuf)
和 write(char[] cbuf, int off, int len)
,每次可以寫出字元數組中的數據,用法類似FileOutputStream,代碼使用演示:
1 public class FWWrite { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileWriter fw = new FileWriter("fw.txt"); 5 // 字元串轉換為位元組數組 6 char[] chars = "普天之下莫非王土".toCharArray(); 7 8 // 寫出字元數組 9 fw.write(chars); // 普天之下莫非王土 10 11 // 寫出從索引2開始,2個字元。索引2是'之',兩個字元,也就是'之下'。 12 fw.write(b,2,2); // 之下 13 14 // 關閉資源 15 fos.close(); 16 } 17 }
寫出字元串:write(String str)
和 write(String str, int off, int len)
,每次可以寫出字元串中的數據,更為方便,代碼使用演示:
1 public class FWWrite { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象 4 FileWriter fw = new FileWriter("fw.txt"); 5 // 字元串 6 String msg = "普天之下莫非王土"; 7 8 // 寫出字元數組 9 fw.write(msg); //普天之下莫非王土 10 11 // 寫出從索引2開始,2個字元。索引2是'之',兩個字元,也就是'之下'。 12 fw.write(msg,2,2); //之下 13 14 // 關閉資源 15 fos.close(); 16 } 17 }
續寫和換行:操作類似於FileOutputStream。
1 public class FWWrite { 2 public static void main(String[] args) throws IOException { 3 // 使用文件名稱創建流對象,可以續寫數據 4 FileWriter fw = new FileWriter("fw.txt",true); 5 // 寫出字元串 6 fw.write("普天"); 7 // 寫出換行 8 fw.write("\r\n"); 9 // 寫出字元串 10 fw.write("之下"); 11 // 關閉資源 12 fw.close(); 13 } 14 }
結果:
小貼士:
字元流,只能操作文本文件,不能操作圖片,視頻等非文本文件。
當我們單純讀或者寫文本文件時 使用字元流 其他情況使用位元組流
3.IO異常處理
3.1 JK7前的處理
JDK7前處理
之前的示範代碼,我們一直把異常拋出,而實際開發中並不能這樣處理,建議使用try...catch...finally
代碼塊,處理異常部分。
代碼使用演示:
1 public class HandleException1 { 2 public static void main(String[] args) { 3 // 聲明變數 4 FileWriter fw = null; 5 try { 6 //創建流對象 7 fw = new FileWriter("fw.txt"); 8 // 寫出數據 9 fw.write("普天之下莫非王土"); //普天之下莫非王土 10 } catch (IOException e) { 11 e.printStackTrace(); 12 } finally { 13 try { 14 if (fw != null) { 15 fw.close(); 16 } 17 } catch (IOException e) { 18 e.printStackTrace(); 19 } 20 } 21 } 22 }
JDK7的處理
還可以使用JDK7優化後的try-with-resource
語句,該語句確保了每個資源在語句結束時關閉。所謂的資源(resource)是指在程式完成後,必須關閉的對象。
格式:
1 try (創建流對象語句,如果多個,使用';'隔開) { 2 // 讀寫數據 3 } catch (IOException e) { 4 e.printStackTrace(); 5 }
代碼使用演示:
1 public class HandleException2 { 2 public static void main(String[] args) { 3 // 創建流對象 4 try ( FileWriter fw = new FileWriter("fw.txt"); ) { 5 // 寫出數據 6 fw.write("普天之下莫非王土"); //普天之下莫非王土 7 } catch (IOException e) { 8 e.printStackTrace(); 9 } 10 } 11 }
4.緩衝流
4.1概述
緩衝流,也叫高效流,是對4個基本的FileXxx
流的增強,所以也是4個流,按照數據類型分類:
-
-
位元組緩衝流:
BufferedInputStream
,BufferedOutputStream
-
字元緩衝流:
BufferedReader
,BufferedWriter
-
緩衝流的基本原理,是在創建流對象時,會創建一個內置的預設大小的緩衝區數組,通過緩衝區讀寫,減少系統IO次數,從而提高讀寫的效率。
構造方法
-
public BufferedInputStream(InputStream in)
:創建一個 新的緩衝輸入流。 -
public BufferedOutputStream(OutputStream out)
: 創建一個新的緩衝輸出流。
構造舉例,代碼如下:
1 // 創建位元組緩衝輸入流 2 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt")); 3 // 創建位元組緩衝輸出流 4 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));
查詢API,緩衝流讀寫方法與基本的流是一致的,我們通過複製大文件(375MB),測試它的效率。
1.基本流,代碼如下:
1 public class BufferedDemo { 2 public static void main(String[] args) throws FileNotFoundException { 3 // 記錄開始時間 4 long start = System.currentTimeMillis(); 5 // 創建流對象 6 try ( 7 FileInputStream fis = new FileInputStream("jdk8.exe"); 8 FileOutputStream fos = new FileOutputStream("copy.exe") 9 ){ 10 // 讀寫數據 11 int b; 12 while ((b = fis.read()) != -1) { 13 fos.write(b); 14 } 15 } catch (IOException e) { 16 e.printStackTrace(); 17 } 18 // 記錄結束時間 19 long end = System.currentTimeMillis(); 20 System.out.println("普通流複製時間:"+(end - start)+" 毫秒"); 21 } 22 } 23 24 十幾分鐘過去了...
2.