在Java中IO操作是有相應步驟的,以文件操作為例,主要的操作流程如下:
- 使用File類打開一個文件;
- 通過字節流或字符流的子類指定輸出的位置;
- 進行讀/寫操作
- 關閉輸入/輸出
字節流和字符流
區別:
- 讀寫單位:顧名思義,字節流以字節(byte)為讀寫單位,而字符流以字符為讀寫單位,根據碼表映射字符,一次可能讀入多個字符。
- 處理對象:字節流可以處理所有類型的數據(包括圖片等),而字符流只能處理字符類型的純文本數據。
- 字節流:一次寫入或讀出8位二進制。
- 字符流:一次寫入或讀出至少8位二進制。不同的字符所占的字節是不同的。
ASCII碼:
一個英文字母(不分大小寫)占一個字節的空間,一個中文漢字占兩個字節的空間。一個二進制數字序列,在計算機中作為一個數字單元,一般為8位二進制數,換算為十進制。最小值0,最大值255。如一個ASCII碼就是一個字節。
UTF-8編碼:
一個英文字符等於一個字節,一個中文(含繁體)等於三個字節。
Unicode編碼:
一個英文等於兩個字節,一個中文(含繁體)等於兩個字節。
1、字節流
字節輸出流OutputStream
OutputStream是整個IO包中字節輸出流的最大父類,作為一個抽象類,如果要使用此類,則必須通過子類實例化對象。
OutputStream類的常用方法:
方法 | 返回類型 | 描述 | 備注 |
---|---|---|---|
close() | void | 關閉輸出流 | |
flush() | void | 刷新緩沖區 | |
write(byte[] b) | void | 將一個byte數組寫入數據流 | |
write(byte[] b, int off, int len) | void | 將一個指定范圍的byte數組寫入數據流 | |
write(int b) | void | 將一個字節數據寫入數據流 | 抽象方法abstract |
現在要操作的是一個文件,可以使用FileOutputStream類,通過向上轉型后,可以為OutputStream實例化。
字節輸入流InputStream
InputStream與OutputStream類一樣,也是一個抽象類,必須依靠其子類。
InputStream類的常用方法:
方法 | 返回類型 | 描述 | 備注 |
---|---|---|---|
available() | int | 可以取得輸入文件的大小 | |
close() | void | 關閉輸入流 | |
read(byte[] b) | int | 將內容讀到byte數組中,同時返回讀入的個數 | |
read() | int | 讀取內容,以數字的方式讀取 | 抽象方法abstract |
現在要操作的是一個文件,可以使用FileInputStream類,通過向上轉型后,可以為InputStream實例化。
實例
注意:由於在 windows 下,文件的路徑使用的是“\”方式,但是反斜杠在字符串中被解析為轉義符,所以一般來講我們需要用“/”來替代“\”。當然,File.separator 是一樣的效果。
如 在 windows 下的地址 F:\大學,我們就可以等價為 F:/大學,或 F:\\大學,或 F: + File.separator + 大學。
1、將數據寫到TXT中
// 1. 使用File類打開一個文件;
File file = new File("d:" + File.separator + "test.txt"); //聲明File對象
// 2. 通過字節流或字符流的子類指定輸出的位置
OutputStream fos = new FileOutputStream(file); // 若是追加文件內容,則(file, true)
// 3. 進行寫操作
String str = "你好,世界";
fos.write(str.getBytes("UTF-8")); //將字符串變成字節byte數組,使用UTF-8編碼
// 4. 關閉輸出
fos.close();
2、將TXT導入到內存
// 1. 使用File類打開一個文件;
File file = new File("d:" + File.separator + "test.txt");
// 2. 通過字節流或字符流的子類指定輸出的位置
InputStream fis = new FileInputStream(file);
// 3. 進行讀操作
byte[] buf = new byte[1024]; // 所有的內容讀到此數組中臨時存放
int len; //用於記錄讀取的數據個數
String myStr = "";
while((len = fis.read(buf)) != -1) { //將內容讀到byte數組中,同時返回個數,若為-1,則內容讀到底
myStr += new String(buf, 0, len, "UTF-8");
}
// 4. 關閉輸出
fis.close();
System.out.println(myStr);
2、字符流
緩沖區:可以簡單地理解為一段內存區域。
某些情況下,如果一個程序頻繁地操作一個資源(如 文件或數據庫),則性能會很低,此時為了提升性能,就可以將一部分數據暫時讀入到內存的一塊區域中,以后直接從此區域中讀取數據即可,因為讀取內存速度會比較快,這樣可以提升程序的性能。
注意:字符流使用了緩沖區,而字節流沒有使用緩沖區。
在字符流的操作中,所有的字符都是在內存中形成的,在輸出前會將所有的內容暫時保存在內存之中,所以使用了緩沖區暫存數據。
字符輸出流Writer
Writer是一個字符流的輸出類,作為一個抽象類,如果要使用此類,則肯定要使用其子類。
Writer類的常用方法:
方法 | 返回類型 | 描述 | 備注 |
---|---|---|---|
close() | void | 關閉輸出流 | abstract |
write(String str) | void | 將字符串輸出 | |
write(char[] cbuf) | void | 將字符數組(緩沖區)輸出 | |
flush() | void | 強制性清空緩存 | abstract |
此時如果是向文件中寫入內容,應該使用FileWriter子類。
字符輸入流Reader
Reader是使用字符的方式從文件中取出數據,作為一個抽象類,如果要使用此類,則肯定要使用其子類。
Reader類的常用方法:
方法 | 返回類型 | 描述 | 備注 |
---|---|---|---|
close() | void | 關閉輸入流 | abstract |
read() | int | 讀取單個字符 | |
read(char[] cbuf) | int | 將內容讀到字符數組中,返回讀入的長度 |
此時如果是向文件中讀取內容,應該使用FileReader子類。
實例
1、將數據寫到TXT中
// 1. 使用File類打開一個文件;
File file = new File("d:" + File.separator + "test.txt");
// 2. 通過字節流或字符流的子類指定輸出的位置
Writer fw = new FileWriter(file);
// 3. 進行寫操作
fw.write(str);
// 4. 關閉輸出
fw.close();
2、將TXT導入內存中
// 1. 使用File類打開一個文件;
File file = new File("d:" + File.separator + "test.txt");
// 2. 通過字節流或字符流的子類指定輸出的位置
Reader fr = new FileReader(file);
// 3. 進行讀操作
char[] buf2 = new char[1024]; // 所有的內容讀到此數組中臨時存放
int len2;
String myStr2 = "";
while((len2 = fr.read(buf)) != -1) {
myStr2 += new String(buf2, 0, len2); //將緩沖區buf2數組中的0到len2字符串讀出
}
// 4. 關閉輸入
fw.close();
System.out.println(myStr2);
3、轉換流
整個IO包實際上分為字節流和字符流,但是除了這兩個流之外,還存在一組字節流-字符流的轉換類。
- OutputStreamWriter:是Writer的子類,將輸出的字符流變為字節流(字符流→字節流),即 將一個字符流的輸出對象變為字節流的輸出對象。
- InputStreamReader:是Reader的子類,將輸入的字節流變為字符流,(字節流→字符流),即 將一個字節流的輸入對象變為字符流的輸入對象。
提示:
FileOutputStream 是 OutputStream 的直接子類
FileInputStream 也是 OutputStream 的直接子類
而 FileWriter 並不直接是 Writer 的子類,而是 OutputStreamWriter 的之類
而 FileReader 並不直接是 Reader 的子類,而是 InputStreamReader 的之類
所以,不管是使用字節流還是字符流實際上最終都是以字節的形式操作輸入/輸出流的。
PS : 所有的文件在硬盤或在傳輸時都是以字節的方式進行的,包括圖片等都是按字節的方式存儲的,而字符是只有在內存中才會形成,所以在開發中,字節流使用較為廣泛。
結論:只要是處理純文本數據,就優先考慮使用字符流。除此之外都使用字節流。