Java 內部使用 Unicode(UTF-16),但外部世界(文件、數據庫、網絡、操作系統)常使用 GB2312、GBK、Big5、ISO-8859-1 等編碼。編碼不一致是亂碼的根本原因。本文從編碼基礎、JVM 處理流程、實戰場景三個維度系統剖析並給出解決方案。
char/String 內部即 UTF-16。源碼 → 編譯器 → 字節碼 → JVM → 操作系統 → 顯示設備
javac -encoding GBK 指定源文件編碼,否則編譯器用平台默認編碼(中文 Windows 為 GBK,Linux 為 ISO-8859-1),導致字面量常量池錯誤。String s1 = request.getParameter("keyword");
String s2 = new String(s1.getBytes("ISO-8859-1"), "GBK");
原理:Servlet 容器默認用 ISO-8859-1 解碼 URL/表單數據,實際為 GBK 字節,需「按原編碼取回字節,再按正確編碼解碼」。
<%@ page contentType="text/html;charset=gb2312" %>
設置響應 Content-Type,容器自動將 Unicode 字符串按指定編碼輸出。
ResultSet.getString() 獲取的字符串可能按 ISO-8859-1 解碼,需同樣 getBytes("ISO-8859-1") 再按 GBK 解碼。PreparedStatement.setString() 前將字符串按 ISO-8859-1 編碼為字節,再構造字符串傳給 JDBC(或配置驅動 characterEncoding=GBK)。客戶端無 JDK、只有瀏覽器 JRE 時,sun.awt.windows.awtLocalization_zh_CN 資源缺失,導致 file.encoding 退回 8859_1,中文轉換失敗。解決:打包必要的 sun.io.CharToByteDoubleByte.class 或升級 JDK 1.2+(內建完整編碼支持)。
sun.io.ByteToCharGB2312、CharToByteBig5 等轉換類存在映射錯誤(如「節」「樂」轉 Big5 變「?」)。HttpUtils.parseName 未考慮雙字節,直接將 %XX 視為單字符,導致中文參數截斷。修正:修改源碼重打包 servlet.jar。javac -encoding UTF-8 或 IDE 設置項目編碼。-Dfile.encoding=UTF-8(慎用,影響日誌等)。URIEncoding="UTF-8"、useBodyEncodingForURI="true"。characterEncoding=utf8&useUnicode=true。response.setCharacterEncoding("UTF-8") 及 Content-Type。<form accept-charset="UTF-8">,頁面 <meta charset="UTF-8">。中文亂碼本質是編碼協議不匹配。Java 提供了完整的轉換 API,關鍵在於明確每個邊界的編碼約定,並統一強制執行。世界標準化組織推動 ISO 10646/Unicode 統一編碼,UTF-8 已成事實標準,拥抱 UTF-8 是徹底解決之道。