Java 內部統一使用 UTF-16 (char 型別) 處理字元。亂碼產生於 外部資料進入 JVM (解碼) 或 JVM 資料輸出外部 (編碼) 時,雙方約定的字符集不一致。全鏈路統一編碼是唯一正解。
| 字符集 | 別名/特點 | 中文支援 | 適用場景 |
|---|---|---|---|
| ISO-8859-1 | Latin-1, 單字節, 0x00-0xFF | ❌ 無中文 | HTTP Header 預設、二進位資料「透傳」中轉 |
| GB2312 | 簡體中文國標, 6763 漢字 | ✅ 簡體 | 舊系統、政府專案強制要求 |
| GBK | GB2312 超集, 含繁體/日文, 21003 字 | ✅ 簡繁兼容 | Windows 國內預設、MySQL 預設、舊專案相容 |
| UTF-8 | Unicode 變長編碼, 1-4 字節, ASCII 兼容 | ✅ 全語系 | 現代 Web 標準、國際化首選、資料庫/前端/後端統一 |
鐵律:全棧統一 UTF-8 (資料庫、連線、JVM、前端、容器、框架)。混用編碼必生亂碼。
瀏覽器 (UTF-8 頁面)
→ HTTP Request (URL/Body/Header 編碼)
→ Tomcat Connector (URIEncoding/body encoding)
→ Servlet Filter (request.setCharacterEncoding)
→ Controller/Service (Java String UTF-16)
→ JDBC Driver (connectionCharacterEncoding)
→ MySQL (utf8mb4)
→ 回寫路徑反向...
<!-- JSP 頁面指令,必須在最頂行 -->
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
<!-- HTML5 meta,瀏覽器解碼依據 -->
<meta charset="UTF-8">
<!-- 表單提交指定編碼 (GET 請求 URL 參數編碼由瀏覽器決定,建議全站 POST) -->
<form method="post" accept-charset="UTF-8">...
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
useBodyEncodingForURI="true" />
URIEncoding:解決 GET 請求 URL 參數中文亂碼 (Tomcat 8+ 預設即 UTF-8)。useBodyEncodingForURI:讓 POST Body 編碼也套用到 URI 解析 (慎用,可能有副作用)。web.xml (或 Spring Boot FilterRegistrationBean) 註冊 CharacterEncodingFilter,**必須是 Filter 鏈第一個**:
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name> <!-- 強制覆蓋 request/response 編碼 -->
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
原理:request.setCharacterEncoding("UTF-8") (僅對 Body 有效) + response.setCharacterEncoding("UTF-8") + response.setContentType("text/html;charset=UTF-8")。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 確保 JSON/Xml 輸出 UTF-8
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setDefaultCharset(StandardCharsets.UTF_8);
converters.add(0, converter);
}
}
URL 參數:
jdbc:mysql://host:3306/db?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai
表/庫建立:
CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE t_user (...) DEFAULT CHARSET=utf8mb4;
注意:MySQL utf8 偽 UTF-8 (最多 3 字節),必須用 utf8mb4 存儲 Emoji/生僻字。
前端發送 JSON 時預設 UTF-8,後端 @RequestBody 自動解碼無需額外配置。
若傳統 application/x-www-form-urlencoded,確保 Content-Type: application/x-www-form-urlencoded; charset=UTF-8。
MultipartFilter / StandardServletMultipartResolver 依賴 request 編碼,全域 Filter 已覆蓋。response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")) 解決中文檔名亂碼。request.getCharacterEncoding() 及原始參數。SELECT HEX(col) FROM table 查看實際位元組,對照 UTF-8/GBK 編碼表。Content-Type、瀏覽器檢視編碼選單。ALTER TABLE t CONVERT TO CHARACTER SET utf8mb4; (鎖表風險大,建議雙寫遷移)。GBK2UTF8Filter 針對特定舊介面轉碼,漸進式切換。charset 統一改 UTF-8,舊頁面若不可改,用 CharacterEncodingFilter forceEncoding=false 避免衝突。總結:亂碼不是 Bug,是配置缺失。新專案啟動日即鎖定全棧 UTF-8 (utf8mb4),引入
CharacterEncodingFilter、TomcatURIEncoding、資料庫連線參數三件套,九成九亂碼問題不再出現。