中文亂碼是 Java 開發者的「成長必經之痛」。本文從字符、字符集、編碼三大核心概念入手,梳理 ASCII、ISO-8859-1、GB2312/GBK/GB18030、BIG5、UTF-8/UTF-16 等主流編碼特性,剖析亂碼產生的三大場景,給出「全鏈路統一編碼」工程化解決方案。
| 術語 | 定義 | 例子 |
|---|---|---|
| 字符 | 抽象符號(字母、漢字、表情符號) | '中', 'A', '😀' |
| 字符集 | 字符的有序集合,賦予每字符唯一代碼點 | Unicode (U+4E2D), GB2312 |
| 編碼 | 將代碼點轉換為位元組序列的規則 | UTF-8: E4 B8 AD, GBK: D6 D0 |
| 編碼 | 覆蓋範圍 | 位元組長度 | 典型場景 |
|---|---|---|---|
| ASCII | 128 字符 (英數符) | 1 固定 | 早期系統、協議頭部 |
| ISO-8859-1 | 西歐語言 256 字符 | 1 固定 | HTTP 預設、Java 內部字串傳輸 |
| GB2312 | 6763 漢字 + 符號 | 1-2 | 舊版簡體中文系統 |
| GBK | GB2312 + BIG5 + 擴展 | 1-2 | Windows 簡繁混排 (CP936) |
| GB18030 | Unicode 映射,強制標準 | 1-4 | 中國軟體產品強制認證 |
| BIG5 | 繁體中文 13000+ 字 | 1-2 | 台灣/香港舊系統 |
| UTF-8 | 全 Unicode (149k+ 字符) | 1-4 變長 | **現代 Web/資料庫/檔案/傳輸首選** |
| UTF-16 | 全 Unicode | 2/4 固定/代理對 | Java char/String 內部表示、Windows API |
String 內部採用 **UTF-16** (JDK 9+ 壓縮為 Latin1/UTF-16)。FileReader, InputStreamReader, HttpServletRequest) **必須顯式指定 charset**,否則退回到平台預設 (Windows=GBK, Linux=UTF-8) → 亂碼根源。// 讀取:指定源檔編碼
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(file), "GBK"))) { ... }
// 寫入:統一輸出 UTF-8
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) { ... }
// 全局 Filter (Spring Boot: server.servlet.encoding.charset=UTF-8)
request.setCharacterEncoding("UTF-8"); // POST 請求體
response.setCharacterEncoding("UTF-8"); // 響應體
response.setContentType("text/html;charset=UTF-8");
// 發送端:URLEncoder.encode(param, "UTF-8")
// 接收端:Tomcat 8+ 需在 server.xml Connector 加 useBodyEncodingForURI="true" URIEncoding="UTF-8"
// 歷史遺留 GET 參數 ISO-8859-1 → 目標編碼:
String fixed = new String(param.getBytes("ISO-8859-1"), "UTF-8");
javac -encoding UTF-8 / Maven: <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>。characterEncoding=utf8mb4, useUnicode=true)。URIEncoding="UTF-8"、Spring Boot server.servlet.encoding.*。<meta charset="UTF-8">、JS 檔案 UTF-8。<charset>UTF-8</charset>。// ISO-8859-1 (網路傳輸/錯誤解碼) → 正確編碼
String correct = new String(broken.getBytes("ISO-8859-1"), "UTF-8");
**切記**:此行代碼只能作為臨時搶險,**必須**配合上游編碼統一徹底根治。