首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 字符編碼問題徹底解析:原理、陷阱與最佳實踐

     編輯:Java 相關知識

編碼問題本質

**亂碼 = 編碼與解碼使用了不同的字符集**

字符串在記憶體中為 Unicode (UTF-16),進行 I/O 時必須明確指定編碼:

  • 編碼:字符 → 字節 (`String.getBytes(charset)` / `Charset.encode()`)
  • 解碼:字節 → 字符 (`new String(bytes, charset)` / `Charset.decode()`)

核心 API 陷阱與正確用法

場景錯誤用法正確用法原因
構造 String`new String(bytes)``new String(bytes, StandardCharsets.UTF_8)`預設使用平台編碼 (Windows=GBK, Linux=UTF-8)
獲取字節`str.getBytes()``str.getBytes(StandardCharsets.UTF_8)`同上,跨平台不一致
文件讀寫`new FileReader(file)``new InputStreamReader(new FileInputStream(file), "UTF-8")`FileReader 無法指定編碼
網絡傳輸`PrintStream` 無編碼`new PrintStream(out, true, "UTF-8")`PrintStream 預設平台編碼
控制台輸出`System.out.println()`IDE/終端設置 UTF-8,JVM 參數 `-Dfile.encoding=UTF-8`控制台編碼可能非 UTF-8

Web 端編碼全鏈路配置

1. JSP/HTML 頁面

<%@ page pageEncoding="UTF-8" contentType="text/html;charset=UTF-8" %>
<meta charset="UTF-8" />

2. Servlet / Filter 統一設置

@WebFilter("/*")
public class EncodingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        chain.doFilter(req, resp);
    }
}

3. Tomcat 配置

<Connector port="8080" URIEncoding="UTF-8" />
<!-- POST 請求體編碼需 Filter 支持,GET 參數由 URIEncoding 決定 -->

4. 數據庫連接

jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

實戰案例:表單提交亂碼追蹤

場景

頁面 `

` 輸入「你好」,後端 `request.getParameter("content")` 顯示亂碼。

追蹤鏈路

  1. 瀏覽器按頁面編碼 (UTF-8) 將「你好」編碼為字節:E4 BD A0 E5 A5 BD (6 字節)
  2. HTTP 請求體攜帶字節流發送
  3. Tomcat 接收,若無 Filter 設置編碼,預設 **ISO-8859-1** 解碼
  4. ISO-8859-1 將每字節直接映射為 Unicode 字符:U+00E4 U+00BD U+00A0 U+00E5 U+00A5 U+00BD
  5. Java String 內部保存這 6 個錯誤字符
  6. 後續處理/存庫/響應均基於錯誤字符

修復

// Filter 中最早設置
request.setCharacterEncoding("UTF-8"); // 僅對 POST 請求體有效
// GET 參數需 Tomcat URIEncoding="UTF-8" 或手動轉碼:
String fixed = new String(wrong.getBytes("ISO-8859-1"), "UTF-8");

Charset 工具類最佳實踐

public final class Charsets {
    public static final Charset UTF_8 = StandardCharsets.UTF_8;
    public static final Charset GBK = Charset.forName("GBK");
    public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
    
    /** 安全編碼:字符串 → 字節數組 */
    public static byte[] encode(String s, Charset cs) {
        return s == null ? new byte[0] : s.getBytes(cs);
    }
    
    /** 安全解碼:字節數組 → 字符串 */
    public static String decode(byte[] bytes, Charset cs) {
        return bytes == null ? "" : new String(bytes, cs);
    }
    
    /** 轉碼:從源編碼轉目標編碼 */
    public static String transcode(String s, Charset src, Charset dest) {
        return decode(encode(s, src), dest);
    }
    
    /** 修復 ISO-8859-1 誤解碼的字符串 */
    public static String fixLatin1(String wrong) {
        return transcode(wrong, ISO_8859_1, UTF_8);
    }
}

常見編碼對照表

字符UTF-8 (十六進制)GBK (十六進制)ISO-8859-1
中E4 B8 ADD6 D0無法表示
文E6 96 87CE C4無法表示
A414141
0303030

避坑指南清單

  • ✅ **所有 `getBytes()` / `new String(bytes)` 顯式指定編碼**
  • ✅ **項目統一 UTF-8**:源文件、資料庫、網絡、文件、日誌
  • ✅ **Filter 位於鏈路最前** 設置請求/響應編碼
  • ✅ **Tomcat `URIEncoding="UTF-8"`** 解決 GET 參數亂碼
  • ✅ **JDBC URL 顯式 `characterEncoding=UTF-8`**
  • ✅ **屬性文件 `.properties` 改用 `.yml` 或確保 UTF-8 保存** (Java 9+ 支持 UTF-8 properties)
  • ✅ **第三方庫輸入輸出邊界** 確認其編碼假設 (如 FastJson、POI、HttpClient)
  • ✅ **單元測試覆蓋中文場景**:`assertEquals("你好", decode(encode("你好", UTF_8), UTF_8))`

排查思路

  1. 確認**數據在哪一層變錯**:前端 → 網絡 → Tomcat → Filter → Servlet → 業務 → DB → 返回
  2. 使用**十六進制工具**查看原始字節:`bytes[i] & 0xFF` 打印
  3. **二分法驗證**:硬編碼字符串測試後端邏輯,排除前端/網絡因素
  4. 檢查**異常處理路徑**是否繞過編碼設置
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved