中文亂碼是 Java 開發者繞不開的坑。本質上,亂碼源於 字節流與字符流轉換時編碼不一致。Java 內部使用 UTF-16 (char),而外部資源(資料庫、檔案、網路、瀏覽器)多使用 UTF-8、GBK、GB2312 等字節編碼。凡涉及 byte[] ⇄ char/String 轉換之處,若編碼不匹配,必現亂碼。
| 類別 | 典型場景 | 轉換方向 |
|---|---|---|
| 源碼編譯 | .java/.jsp 檔案含中文常量/註釋 | 檔案編碼 → JVM 內部編碼 |
| 外部交互 | 表單提交、頁面輸出、資料庫讀寫、檔案讀寫、網路流 | byte[] ⇄ String |
系統中所有「字節 ↔ 字符」轉換點,必須顯式指定編碼,不可依賴平台預設值(Charset.defaultCharset() 隨作業系統、啟動參數、容器配置而變)。
// 請求:必須在讀取參數前設置
request.setCharacterEncoding("UTF-8"); // 或 GBK
// 響應:設置 Content-Type 包含 charset
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
最佳實踐:使用 Filter 統一攔截,配置於 web.xml 首位:
@WebFilter("/*")
public class EncodingFilter implements Filter {
private String encoding = "UTF-8";
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
req.setCharacterEncoding(encoding);
resp.setCharacterEncoding(encoding);
resp.setContentType("text/html;charset=" + encoding);
chain.doFilter(req, resp);
}
}
jdbc:mysql://host/db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull錯誤示範:直接使用 FileReader/FileWriter(隱式使用平台預設編碼)。
正確做法:使用 InputStreamReader/OutputStreamWriter 顯式指定編碼:
// 讀取 GBK 編碼檔案
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream(file), "GBK"))) {
String line;
while ((line = br.readLine()) != null) { /* ... */ }
}
// 寫入 UTF-8 檔案
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))) {
bw.write(content);
}
server.xml 中 <Connector URIEncoding="UTF-8" />-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8組件內部用 new String(byte[]) 隱式用平台預設編碼解析上傳參數。Resin 註冊為 Windows 服務後,預設編碼從 Cp1252 變為 GBK,導致解析錯誤。修正:修改組件源碼,顯式傳入 charset 參數。
file.encoding=UTF-8亂碼不可怕,可怕的是「靠運氣」。建立「編碼顯式化、全鏈路一致、代碼審查強制」的工程文化,才是根治之道。