亂碼是 Java 開發者的噩夢。本文從位元組層面實測三種常見編碼差異,建立「編碼必須對應」的鐵律,並給出生產環境完整解決方案。
Eclipse 專案預設編碼 GBK,測試字串 "我愛ABC"(中英混合)。
public class Demo1 {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "我愛ABC";
// GBK:中文 2 bytes,英文 1 byte
printBytes("GBK", s.getBytes("gbk"));
// UTF-8:中文 3 bytes,英文 1 byte
printBytes("UTF-8", s.getBytes("utf-8"));
// UTF-16BE:中英文皆 2 bytes(大端序)
printBytes("UTF-16BE", s.getBytes("utf-16be"));
// 錯誤示範:以專案預設 GBK 解碼 UTF-16BE 位元組 → 亂碼
String wrong = new String(s.getBytes("utf-16be"));
System.out.println("錯誤解碼:" + wrong);
// 正確:指定對應編碼
String right = new String(s.getBytes("utf-16be"), "utf-16be");
System.out.println("正確解碼:" + right);
}
static void printBytes(String label, byte[] bytes) {
System.out.print(label + ": ");
for (byte b : bytes) System.out.printf("%02X ", b & 0xFF);
System.out.println();
}
}
GBK: CE D2 C3 C7 41 42 43 // "我愛" 各 2 bytes,ABC 各 1 byte
UTF-8: E6 88 91 E6 84 9B 41 42 43 // "我愛" 各 3 bytes
UTF-16BE: 62 11 61 1B 00 41 00 42 00 43 // 所有字元固定 2 bytes
| 編碼 | 中文 | 英文/數字 | 適用場景 |
|---|---|---|---|
| GBK/GB18030 | 2 bytes | 1 byte | 中文 Windows 系統、舊專案相容 |
| UTF-8 | 3 bytes | 1 byte | Web 標準、國際化、資料庫、檔案傳輸首選 |
| UTF-16BE/LE | 2 bytes | 2 bytes | Java 內部 char 型別、特定協定 |
new String(bytes)、FileReader、System.out.println 依賴 file.encoding,跨平台部署極易出錯-Dfile.encoding=UTF-8、Maven <properties><project.build.sourceEncoding>UTF-8</...>CREATE DATABASE ... CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,連線 URL 加 ?characterEncoding=utf8<meta charset="UTF-8">// 讀寫檔案
try (Reader r = new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8);
Writer w = new OutputStreamWriter(new FileOutputStream(f), StandardCharsets.UTF_8)) { ... }
// HTTP Servlet
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// JDBC
url = "jdbc:mysql://host/db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC";
spring.http.encoding.charset=UTF-8、spring.http.encoding.force=trueserver.xml:<Connector URIEncoding="UTF-8" ... /><charset>UTF-8</charset>// 線上排查:印出原始 bytes 判斷實際編碼
byte[] raw = ...;
for (byte b : raw) System.out.printf("%02X ", b & 0xFF);
// 對照碼表確認編碼,再決定正確解碼方式
編碼問題本質是位元組序列與字元映射表的對應關係。只要堅持「全鏈路顯式指定 UTF-8」,99% 亂碼問題消失。遇到存量非 UTF-8 系統,邊界處統一轉碼(讀入即轉 UTF-8,寫出再轉目標編碼),內部統一用 Unicode (String) 處理,是架構層面的終極解法。