我們都知道在計算機中,無論是文本、圖片、音頻還是視頻,所有的文件都是以二進制(字節)形式存在的,IO流中針對字節的輸入輸出提供了一系列的流,統稱為字節流。字節流是程序中最常用的流。在JDK中,提供了兩個抽象類InputStream和OutputStream,它們是字節流的頂級父類,所有的字節輸入流都繼承自InputStream,所有的字節輸出流都繼承自OutputStream。既然二者是抽象類,那么就不能實例化,都是依賴於具體繼承它們的子類去實現。但二者抽象類中也包含它們各自的方法,如果我們先了解清楚抽象類中每一個方法的含義,那么對於后續具體的子類將會有非常大的幫助。
1、字節輸入流InputStream
InputStream是所有字節輸入流的父類,定義了所有字節輸入流都具有的共同特征。其內部提供的方法如下:
上圖中的三個read()方法都是用來讀數據的。
- int read():每次讀取一個字節,返回讀取到的字節。
- int read(byte[] b):每次讀取 b 數組長度的字節數,然后返回讀取的字節的個數 [注意與read() 方法的區別],讀到末尾時返回-1。
- int read(byte[] b,int off,int len):每次讀取 b 數組長度的字節數,從數組b 的索引為off 的位置開始,長度為len個字節。
小貼士:close方法,當完成流的操作時,必須調用此方法,釋放系統資源。
其中InputStream的實現類FileInputStream是專門用於讀取文件中的數據,通過它將目標設備上的數據讀入到程序。
FileInputStream的構造方法:
- FileInputStream(File file): 通過打開與實際文件的連接來創建一個 FileInputStream ,該文件由文件系統中的 File對象 file命名。
- FileInputStream(String name): 通過打開與實際文件的連接來創建一個 FileInputStream ,該文件由文件系統中的路徑名 name命名。
FileInputStream讀入文件舉例(讀取的hello.txt文件內容是 ABCDEFGH):
package com.thr; import java.io.*; /** * @author Administrator * @date 2020-02-21 * @desc 字節輸入流FileInputStream */ public class FileInputStreamTest { public static void main(String[] args) { //定義輸入流 FileInputStream fis =null; try { //1、創建文件對象 File file = new File("D:\\IO\\hello.txt"); //2、創建輸入流對象 fis = new FileInputStream(file); //用定義字節數組,作為裝字節數據的容器 byte[] buffer =new byte[5]; int len;//記錄每次讀取的字節個數 //System.out.println(fis.read(buffer)); while ((len=fis.read(buffer))!=-1){ //轉成String型,否則輸出ASCII碼 String str=new String(buffer,0,len); System.out.println(str); } } catch (IOException e) { e.printStackTrace(); } finally { //釋放資源 try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } 運行結果: ABCDE FGH
如果我們在讀取的hello.txt文件中添加幾個中文字,再次用同樣的代碼來運行看一下會發生什么情況(hello.txt內容為 ABCDEFGH中國人):
運行結果的截圖:
可以發現運行的結果出現了亂碼,這是因為每次讀取五個字符,而一個utf-8的中文則占了3個字節,而第二次讀取的時候【中】這個字的字節數並沒有讀完,而【國】字全部讀完了,所以【中】和【人】字都出現了亂碼而【國】沒有出現亂碼。這里畫個圖好理解一點。
為了避免可能出現的亂碼,要么把 byte[] 數組容量增大,要么就使用字符輸入輸出流來處理(對於純文本最好使用字符流)。
2、字節輸出流OutputStream
OutputStream是所有字節輸出流的父類,定義了所有字節輸出流都具有的共同特征。其內部提供的方法如下:
上圖中的三個write()方法都是用來寫數據的。
- void write(int b):把一個字節寫入到文件中。
- void write(byte[] b):把數組b 中的所有字節寫入到文件中。
- void write(byte[] b,int off,int len):把數組b 中的字節從 off 索引開始的 len 個字節寫入到文件中。
其中OutputStream的實現類FileOutputStream是門用於讀出文件中的數據,通過它將數據從程序輸出到目標設備。
FileOutputStream的構造方法:
- FileOutputStream(File file):創建文件輸出流以寫入由指定的 File對象表示的文件。
- FileOutputStream(String name): 創建文件輸出流以指定的名稱寫入文件。
- FileOutputStream(String name,boolean append): 創建文件輸出流以指定的名稱寫入文件,每次輸出文件是否繼續拼接。
FileOutputStream輸出文件舉例:
package com.thr; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; /** * @author Administrator * @date 2020-02-21 * @desc 字節輸出流FileOutputStream */ public class FileOutputStreamTest { public static void main (String[] args) { //定義字節輸出流 FileOutputStream fos =null; try { //1、創建文件對象 File file = new File("D:\\IO\\hello.txt"); //2、創建輸出流對象 fos = new FileOutputStream(file); fos.write(97); //后面的 \r\n表示回車換行 fos.write("中國人!\r\n".getBytes()); //從索引2開始輸出4個字節 fos.write("ABCDEFGH".getBytes(),2,4); } catch (IOException e) { e.printStackTrace(); } finally { //釋放資源 try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
運行后結果是: a中國人!CDEF 。而如果我們多次運行測試代碼會發現,每次運行完程序之后文件大小並沒有改變,說明每次運行都創建了一次新的輸出流對象,每次都清空目標文件中的數據。那么要如何才能保留目標文件中數據,還能繼續追加新數據呢?其實很簡單,如下兩個FileOutputStream構造方法:
- public FileOutputStream(File file, boolean append)
- public FileOutputStream(String name, boolean append)
這兩個構造方法,第二個參數中都需要傳入一個boolean類型的值,true表示追加數據,false表示不追加也就是清空原有數據。
3、字節流拷貝文件
在應用程序中,IO流通常都是成對出現的,即輸入流和輸出流一起使用。而文件的拷貝就需要通過輸入流來讀取文件中的數據,通過輸出流將數據寫入文件。
package com.thr; import java.io.*; /** * @author Administrator * @date 2020-02-21 * @desc 將文件從D盤拷貝到C盤 */ public class FileInputOutputStreamTest { public static void main(String[] args) { //定義輸入流 FileInputStream fis =null; //定義輸出流 FileOutputStream fos=null; //idea的try-catch快捷鍵Ctrl+Alt+T try { //創建文件對象 File f1 = new File("D:\\IO\\1.jpg"); File f2 = new File("C:\\2.jpg"); //創建輸入輸出流對象 fis = new FileInputStream(f1); fos = new FileOutputStream(f2); //定義讀取的大小 byte [] bytes = new byte[1024]; int len; //讀取並且讀出 while((len=fis.read(bytes))!=-1){ fos.write(bytes, 0, len); } System.out.println("拷貝成功..."); } catch (IOException e) { e.printStackTrace(); } finally { //釋放 try { fis.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }