JVM 內部統一使用 UTF-16 表示字元,所有編碼轉換發生在 JVM 與外部系統 (檔案、網路) 邊界。本文詳細解析 Java IO 體系、Reader/Writer 隱式轉換、OutputStreamWriter 顯式指定編碼的正確用法。
char han = '漢'; // 記憶體中實際為 0x6C49 (UTF-16)
System.out.format("%x", (short) han); // 輸出: 6c49
char han2 = 0x6c49; // 以 UTF-16 編碼指定字元
System.out.println(han2); // 輸出: 漢
結論:正確讀入字元後,記憶體中一定是 UTF-16 編碼值,無其他可能。

| 類別 | 面向 | 特性 | 適用場景 |
|---|---|---|---|
| InputStream/OutputStream | 位元組 | 二進制完全一致,無轉換 | 圖片、影音、二進制檔案 |
| Reader/Writer | 字元 | 隱式使用系統預設編碼轉換 | 文字檔案、跨平台文字處理 |
file.encoding 系統屬性 (中文 Windows 為 GBK)位元組流與字元流之間的轉接器,**唯一可指定編碼的實例化時機**
try (PrintWriter out = new PrintWriter(
new OutputStreamWriter(
new FileOutputStream("c:/utf-8.txt"), "UTF-8"))) {
out.write("漢");
} catch (IOException e) {
throw new RuntimeException(e);
}
使用 UltraEdit 以十六進制開啟 utf-8.txt:
E6 B1 89 // "漢" 的 UTF-8 編碼 (3 bytes)
| 錯誤代碼 | 問題 | 修正 |
|---|---|---|
| new FileWriter("a.txt") | 使用系統預設編碼 (GBK),跨平台亂碼 | new OutputStreamWriter(new FileOutputStream("a.txt"), "UTF-8") |
| new FileReader("a.txt") | 假設系統預設編碼,讀取 UTF-8 檔案亂碼 | new InputStreamReader(new FileInputStream("a.txt"), "UTF-8") |
| new String(bytes) | 使用系統預設編碼解碼位元組陣列 | new String(bytes, "UTF-8") |
| str.getBytes() | 使用系統預設編碼編碼字串 | str.getBytes("UTF-8") |
// HTTP 請求讀取
BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
// HTTP 回應寫入
OutputStreamWriter writer = new OutputStreamWriter(
connection.getOutputStream(), "UTF-8");
牢記「JVM 內部 UTF-16,邊界處轉換」,養成**所有 IO 操作顯式指定編碼**習慣,是解決亂碼問題的根本之道。