????????在博主的上一篇博文中,詳細的介紹了“IO”流中最基本的一些知識,包括基本的常見的字節流和字符流,以及對應的緩沖流,對于“IO”流基礎知識相對薄弱的同學可以先去看博主的上一篇博文Java學習之——萬字詳解“IO流”中基本的字節流和字符流?后在回頭來看這篇進階文章效果會更好。
????????Java IO流中的“進階”流:轉換流、序列化流、打印流和壓縮流。它們構建在基礎字節流和字符流之上,提供了更強大、更便捷的功能。
一、轉換流
1.核心概念:為什么需要轉換流?
要理解轉換流,首先要明白 Java I/O 流體系中的兩個核心分支:
- 字節流:以字節(8 bit)為基本單位,處理所有類型的數據(如圖片、視頻、音頻等二進制文件,也包括文本文件)。基類是?
InputStream
?和?OutputStream
。 - 字符流:以字符(16 bit,一個char)為基本單位,專門為方便處理文本數據而設計。它底層仍然是字節操作,但會自動處理字符編碼。基類是?
Reader
?和?Writer
。
問題:我們經常遇到這樣的場景——一個字節流(例如,來自網絡、文件的?FileInputStream
?或?Socket.getInputStream()
)傳輸的是文本內容。如果我們直接使用字節流讀取,得到的是一個個字節,我們需要自己將這些字節按照正確的字符編碼(如 UTF-8, GBK)拼接和轉換成字符,非常麻煩且容易出錯。
解決方案:轉換流(InputStreamReader
?/?OutputStreamWriter
)。
它們的作用就是作為一座“橋梁”,將底層的字節流?轉換為頂層的字符流,并在轉換過程中完成字節到字符的編碼解碼工作。
InputStreamReader
: 將一個字節輸入流(InputStream
)轉換為一個字符輸入流(Reader
)。【解碼:字節 -> 字符】OutputStreamWriter
: 將一個字符輸出流(Writer
)轉換為一個字節輸出流(OutputStream
)。【編碼:字符 -> 字節】
2.InputStreamReader 詳解
核心功能:InputStreamReader是?Reader的子類。它包裹著一個字節輸入流(InputStream
),并讀取字節,然后使用指定的或默認的字符集將其解碼為字符。
關鍵構造函數:
// 使用默認字符集創建一個 InputStreamReader
InputStreamReader(InputStream in)// 使用指定的字符集名稱創建一個 InputStreamReader
// 字符集如:"UTF-8", "GBK", "ISO-8859-1"
InputStreamReader(InputStream in, String charsetName)// 使用指定的 Charset 對象創建一個 InputStreamReader
InputStreamReader(InputStream in, Charset cs)
工作原理:
當你調用?InputStreamReader
?的?read()
?方法時,會發生以下步驟:
- 它從內部的?
InputStream
?中讀取一個或多個字節。 - 這些字節根據構造時指定的字符集(Charset)進行解碼。
- 將解碼后的結果以一個?
char
(或多個?char
?放入數組)的形式返回。
代碼示例:
????????假設我們有一個文本文件 text.txt,其編碼是 GBK。如果我們用默認字符集(通常是 UTF-8)的 FileReader(它是 InputStreamReader 的子類)讀取,可能會亂碼。使用 InputStreamReader 指定編碼可以完美解決。
import java.io.*;public class InputStreamReaderDemo {public static void main(String[] args) {try (// 1. 創建一個字節流 FileInputStreamFileInputStream fis = new FileInputStream("text.txt");// 2. 創建一個轉換流 InputStreamReader,并指定編碼為 GBKInputStreamReader isr = new InputStreamReader(fis, "GBK");// 3. 為了高效,通常用 BufferedReader 包裹 InputStreamReaderBufferedReader br = new BufferedReader(isr)) {String line;// 此時讀取到的字符已經是正確解碼后的文本,不會亂碼while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}
}
3.OutputStreamWriter 詳解
????????OutputStreamWriter 是 Writer 的子類。它接收字符,然后使用指定的或默認的字符集將其編碼為字節,并寫入到底層的字節輸出流(OutputStream)中。
關鍵構造函數
// 使用默認字符集創建一個 OutputStreamWriter
OutputStreamWriter(OutputStream out)// 使用指定的字符集名稱創建一個 OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName)// 使用指定的 Charset 對象創建一個 OutputStreamWriter
OutputStreamWriter(OutputStream out, Charset cs)
工作原理
當你調用?OutputStreamWriter
?的?write()
?方法時,會發生以下步驟:
- 你傳入的字符(或字符串)根據構造時指定的字符集(Charset)進行編碼。
- 編碼后得到的一個或多個字節被寫入到底層的?
OutputStream
?中。 OutputStream
?最終將這些字節寫入到目標(文件、網絡等)。
我們想要將一個字符串以?GBK
?編碼寫入到文件中。
import java.io.*;public class OutputStreamWriterDemo {public static void main(String[] args) {try (// 1. 創建一個字節流 FileOutputStreamFileOutputStream fos = new FileOutputStream("output.txt");// 2. 創建一個轉換流 OutputStreamWriter,并指定編碼為 GBKOutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");// 3. 為了高效和方便,通常用 BufferedWriter 包裹BufferedWriter bw = new BufferedWriter(osw)) {String content = "你好,世界!";bw.write(content);bw.newLine();bw.write("This is a test.");} catch (IOException e) {e.printStackTrace();}// 最終文件 output.txt 的編碼將是 GBK}
}
與 FileReader / FileWriter 的關系
-
FileReader
?本質上是?InputStreamReader
?的子類,它簡化了文件的讀取,但無法指定編碼,只能使用默認編碼。FileWriter
?同理。 -
結論:在需要明確指定編碼的場合(絕大多數國際化和跨平臺場景),不要直接使用?
FileReader
?和?FileWriter
,而應該使用?InputStreamReader
?和?OutputStreamWriter
?并手動指定編碼。FileReader/FileWriter
?僅適用于處理與系統默認編碼一致的文件。