Java 內部統一用 UTF-16 (Unicode) 存儲字符。字符流經「輸入流 → 解碼為 Unicode → 內存 → 編碼為目標編碼 → 輸出流」兩次轉碼。任一環節編碼不一致,即產生亂碼。本文按「控制台、JSP 靜態/動態、Request 參數 (GET/POST)、數據庫」五大場景,給出原理分析、排查步驟、標準解法。
String text = "大家好";
System.out.println(text); // 控制台顯示 ???? 或
System.out.println 使用 PrintStream,默認按 平台默認編碼 (GBK) 將 Unicode 編碼為字節輸出;Run Configurations → Common → Encoding 勾選 GBK (需先在 Workspace 設置添加 GBK 選項),或代碼中指定 System.setOut(new PrintStream(System.out, true, "UTF-8")) 統一輸出編碼。
pageEncoding 屬性;contentType="text/html;charset=UTF-8",寫入 HTTP Header;<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
三處 UTF-8 必須一致,且 pageEncoding 告知 Tomcat 讀取編碼,contentType 告知瀏覽器解碼編碼。
BufferedReader reader = new BufferedReader(new FileReader("D:\\test.txt")); // FileReader 默認平台編碼 GBK
String content = reader.readLine();
文件為 UTF-8,但 FileReader 硬編碼使用平台默認編碼 (GBK) 讀取,導致內存中 Unicode 錯誤。
InputStreamReader isr = new InputStreamReader(new FileInputStream("D:\\test.txt"), "UTF-8");
BufferedReader reader = new BufferedReader(isr);
或啟動參數 -Dfile.encoding=UTF-8 (不推薦,影響全局)。
| 維度 | GET | POST |
|---|---|---|
| 參數位置 | URL Query String | Request Body |
| 瀏覽器編碼 | IE6: 系統默認 GBK; IE7+: 頁面編碼; 現代瀏覽器: 頁面編碼 | 頁面編碼 (由 Content-Type charset 決定) |
| Tomcat 解碼 | URIEncoding (server.xml),默認 ISO-8859-1 | request.setCharacterEncoding() (需在首次 getParameter 前調用) |
| Filter 可攔截 | 無效 (Tomcat 早期解析) | 有效 |
server.xml 配置 <Connector URIEncoding="UTF-8" useBodyEncodingForURI="true" />:useBodyEncodingForURI="true" 使 GET 也使用 request.setCharacterEncoding 設置的編碼;CharacterEncodingFilter (Spring 提供 org.springframework.web.filter.CharacterEncodingFilter):request.setCharacterEncoding("UTF-8") 並 response.setCharacterEncoding("UTF-8"),映射 /*,forceEncoding=true;charset=UTF-8;contentType: "application/x-www-form-urlencoded; charset=UTF-8"。若無法升級,需單獨配置 URIEncoding="UTF-8" 處理 GET,Filter 處理 POST,並確保頁面編碼與 URIEncoding 一致 (均為 UTF-8 或均為 GBK)。
jdbc:mysql://host:3306/db?characterEncoding=UTF-8&useUnicode=true&serverTimezone=Asia/Shanghai;utf8mb4 (支持 Emoji);connectionInitSqls 設置 SET NAMES utf8mb4。堅持「指定編碼讀取 → Unicode → 指定編碼輸出」原則,拒絕 new String(bytes, "ISO-8859-1") 類暴力轉碼,從源頭消除亂碼。