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

Java 中文亂碼終極解決指南:編碼轉換原理、常見坑點與最佳實務

     編輯:Java 相關知識

前言

中文亂碼是 Java 開發者的經典痛點。本質上是位元組序列與字元編碼不匹配導致。理解 JVM 內部使用 UTF-16 (char),而外部世界(檔案、網路、資料庫、瀏覽器)使用各種編碼(UTF-8, GBK, Big5, ISO-8859-1),掌握解碼/編碼邊界,即可徹底解決。

一、核心概念:Byte ↔ Char 轉換機制

1. 字元與位元組

  • char / String:JVM 內部 **UTF-16** 編碼(2-4 位元組/字元)
  • byte[]:外部儲存/傳輸形式,**編碼相關**(UTF-8: 1-4 bytes, GBK: 1-2 bytes)

2. 關鍵轉換類別

方向類別核心方法
Byte → Char (解碼)InputStreamReader, ByteToCharConverterread(), convertAll()
Char → Byte (編碼)OutputStreamWriter, CharToByteConverterwrite(), convertAll()

3. 手動轉換範例


// 解碼:GBK 位元組 → Unicode 字元
byte[] gbkBytes = {(byte)0xC4, (byte)0xE3}; // "你" 的 GBK 編碼
String encoding = "GBK";
ByteToCharConverter converter = ByteToCharConverter.getConverter(encoding);
char[] chars = converter.convertAll(gbkBytes); // 結果:['你'] (0x4F60)

// 編碼:Unicode 字元 → UTF-8 位元組
char[] uniChars = {'你'}; // 0x4F60
CharToByteConverter conv = CharToByteConverter.getConverter("UTF-8");
byte[] utf8Bytes = conv.convertAll(uniChars); // 結果:[0xE4, 0xBD, 0xA0]

二、七大亂碼產生邊界

  1. 資料庫 ↔ Java:JDBC URL 需指定 characterEncoding、useUnicode=true
  2. 檔案 I/O:new FileReader(file) 使用平台預設編碼 → **必須用** new InputStreamReader(new FileInputStream(file), "UTF-8")
  3. 網路傳輸:HTTP Header Content-Type: text/html; charset=UTF-8
  4. Web 請求參數:request.setCharacterEncoding("UTF-8")(需在讀取參數前呼叫)
  5. Web 回應輸出:response.setContentType("text/html;charset=UTF-8") 或 response.setCharacterEncoding("UTF-8")
  6. JSP 頁面:<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>
  7. 原始碼編譯:javac -encoding UTF-8(否則中文常值依平台編碼編譯,跨平台亂碼)

三、常見錯誤模式與修正

❌ 錯誤 1:String.getBytes() 無指定編碼


// 錯:用平台預設編碼(中文 Windows 為 GBK,Linux 為 UTF-8)
byte[] bytes = str.getBytes(); 

// 正:明確指定
byte[] bytes = str.getBytes(StandardCharsets.UTF_8); // Java 7+

❌ 錯誤 2:new String(byte[]) 無指定編碼


// 錯
String s = new String(bytes); 

// 正
String s = new String(bytes, StandardCharsets.UTF_8);

❌ 錯誤 3:FileReader / FileWriter 直接使用


// 錯:無法指定編碼
FileReader fr = new FileReader("file.txt");
FileWriter fw = new FileWriter("out.txt");

// 正:使用 Stream + Reader/Writer 包裝
try (InputStreamReader isr = new InputStreamReader(new FileInputStream("file.txt"), "UTF-8");
     OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("out.txt"), "UTF-8")) {
    // ...
}

❌ 錯誤 4:Tomcat 8+ GET 參數亂碼

server.xml 設定 <Connector URIEncoding="UTF-8" useBodyEncodingForURI="true" />

四、UTF-8 編碼原理深度解析

Unicode 碼位 → UTF-8 位元組序列:

Unicode 範圍位元組數位元格式範例
U+0000 ~ U+007F10xxxxxxxASCII 相容
U+0080 ~ U+07FF2110xxxxx 10xxxxxx希臘文、西里爾文
U+0800 ~ U+FFFF31110xxxx 10xxxxxx 10xxxxxx中日韓文字 (CJK)
U+10000 ~ U+10FFFF411110xxx 10xxxxxx 10xxxxxx 10xxxxxxEmoji、罕用字

實例:「你」 U+4F60 → UTF-8

二進制:0100 1111 0110 0000 (16 bits)
分組:  0100  111101  100000
填入:  11100100  10111101  10100000
十六進制:E4       BD       A0

五、Spring Boot / Servlet 統一編碼配置

1. Filter 方案(傳統 Servlet)


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

2. Spring Boot 配置


# application.yml
server:
  servlet:
    encoding:
      charset: UTF-8
      enabled: true
      force: true
      force-request: true
      force-response: true

3. Tomcat 連接器設定


<Connector port="8080" protocol="HTTP/1.1"
           URIEncoding="UTF-8"
           useBodyEncodingForURI="true" />

六、資料庫編碼完整鏈路


# MySQL 連接字串
jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Taipei

# 資料庫/資料表/欄位編碼一致性
CREATE DATABASE db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE TABLE t (c VARCHAR(255)) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

關鍵:使用 utf8mb4 而非 utf8(MySQL 的 utf8 只有 3 位元組,不支援 Emoji)。

七、除錯檢查清單

  1. 原始碼檔案編碼 = 編譯指定編碼 = 運行時預期編碼
  2. 所有「邊界」轉換處明確指定編碼
  3. 瀏覽器顯示編碼 = 回應 Header charset = 內容實際編碼
  4. 資料庫連接參數、表結構、欄位編碼三者一致
  5. 日誌檔案編碼與檢視工具一致

總結

中文亂碼本質是編碼協議不一致。核心原則:**邊界處明確指定編碼,全鏈路統一使用 UTF-8 (或 utf8mb4)**。Java 7+ 提供 StandardCharsets 常數,Java 18+ file.encoding 預設 UTF-8,現代開發環境已大幅減少亂碼機率,但理解底層原理仍是排查疑難雜症的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved