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

Java 中文編碼亂碼終極解決指南:從底層原理到工程落地的全鏈路分析

     編輯:Java 程式設計入門

前言:亂碼的本質是編碼不一致

Java 內部統一使用 UTF-16 (Unicode),而外部世界(文件、數據庫、網絡、操作系統)使用各種編碼(GBK、GB2312、Big5、UTF-8、ISO-8859-1 等)。亂碼 = 字節流按錯誤編碼解碼。本文從編碼常識、全鏈路傳輸、典型場景、工程規範四個維度徹底解決中文問題。

一、編碼常識速查表

編碼特點適用場景字節長度
ASCII1 字節,僅英文早期系統1
GB2312國標簡體,6763 漢字中國大陸舊系統漢字 2 字節
GBKGB2312 超集,含繁體Windows 中文默認漢字 2 字節
Big5繁體中文標準台灣/香港舊系統漢字 2 字節
UTF-8Unicode 變長編碼,兼容 ASCII現代 Web、數據庫、文件標準英文1、漢字3
UTF-16Java 內存內部編碼JVM 內部字符處理2 或 4 字節
ISO-8859-1單字節西歐編碼,無中文HTTP 協議默認、舊系統中轉1

二、Java 源碼到顯示的完整鏈路與編碼轉換點

[Java 源文件] 
    │ encoding=GBK/UTF-8
    ▼
[javac 編譯] ──► [.class 字節碼 (Unicode/UTF-8)]
    │ -encoding 參數指定源文件編碼
    ▼
[JVM 類加載] 
    ▼
[運行時字符串操作] ──► [String.getBytes(編碼) / new String(bytes, 編碼)]
    │ 核心轉換點:字節[] ←→ char[]/String
    ▼
[I/O 流操作] ──► [InputStreamReader/OutputStreamWriter 指定 charset]
    │ 文件/網絡/數據庫交互邊界
    ▼
[操作系統/終端/瀏覽器] ──► 顯示設備渲染
    │ 環境默認編碼 (locale/Charset.defaultCharset())

關鍵轉換 API

// 字節數組 → 字符串 (解碼)
String str = new String(bytes, "UTF-8"); // 推薦
String str = new String(bytes, StandardCharsets.UTF_8); // JDK 7+

// 字符串 → 字節數組 (編碼)
byte[] bytes = str.getBytes("UTF-8"); // 推薦
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);

// 流轉換 (高性能、大數據)
Reader reader = new InputStreamReader(inputStream, "UTF-8");
Writer writer = new OutputStreamWriter(outputStream, "UTF-8");

三、四大典型亂碼場景與解決方案

場景 1:JSP/Servlet 請求參數亂碼 (GET/POST 不同)

原因:Tomcat 8+ 默認 UTF-8,舊版本 GET 用 ISO-8859-1。


// 方案 A:Tomcat server.xml 統一設置 (推薦)


// 方案 B:Filter 統一處理 (Spring 已內置 CharacterEncodingFilter)
public class EncodingFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        chain.doFilter(req, resp);
    }
}

// 方案 C:手動修復 (不推薦,治標不治本)
String fixed = new String(param.getBytes("ISO-8859-1"), "UTF-8");

場景 2:數據庫中文存取亂碼

核心原則:JDBC URL 指定編碼,數據庫/表/列編碼一致,連接池層面統一。


// MySQL 8.0+ 推薦配置
jdbc:mysql://localhost:3306/db?characterEncoding=utf8&useSSL=false&serverTimezone=UTC

// 表創建指定編碼
CREATE TABLE t_user (
    name VARCHAR(50)
) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

// 連接池 (HikariCP/Druid) 初始化設置
dataSource.setConnectionInitSql("SET NAMES utf8mb4");

場景 3:文件讀寫亂碼

// 讀取 GBK 文件
try (BufferedReader br = new BufferedReader(
        new InputStreamReader(new FileInputStream("gbk.txt"), "GBK"))) {
    String line;
    while ((line = br.readLine()) != null) { /* 處理 */ }
}

// 寫入 UTF-8 文件 (標準)
try (BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(new FileOutputStream("utf8.txt"), "UTF-8"))) {
    bw.write("中文內容");
}

// JDK 7+ NIO 簡潔寫法
Files.readAllLines(Paths.get("gbk.txt"), Charset.forName("GBK"));
Files.write(Paths.get("utf8.txt"), "中文內容".getBytes(StandardCharsets.UTF_8));

場景 4:HTTP 響應輸出亂碼 (JSP/REST API)


// JSP 頁面指令 (必須在第一行)
<%@ page contentType="text/html;charset=UTF-8" pageEncoding="UTF-8" %>

// Servlet 手動設置
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();

// Spring MVC @ResponseBody 自動處理 (需配置 StringHttpMessageConverter)
@GetMapping("/api")
@ResponseBody
public String api() { return "中文"; } // 默認 UTF-8

四、工程規範:從源頭杜絕亂碼

  1. 統一編碼標準:全項目強制 UTF-8 (或 UTF-8MB4),拒絕 GBK/Big5 混用
  2. IDE 設置:IntelliJ IDEA / Eclipse 設置 Project Encoding = UTF-8,文件模板聲明编码
  3. 構建工具:Maven/Gradle 指定編譯編碼
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
    </properties>
    
  4. 數據庫層:新建庫表默認 utf8mb4,避免 Emoji 存儲截斷
  5. 中間件層:Tomcat/Nginx/Redis/Kafka 統一 UTF-8
  6. 日誌框架:Logback/Log4j2 配置 `UTF-8`
  7. 代碼審查清單:
    • 無 `new String(bytes)` 無參調用
    • 無 `getBytes()` 無參調用
    • 流讀寫顯式指定 Charset
    • HTTP 請求/響應設置 CharacterEncoding

五、疑難雜症診斷工具箱

現象可能原因排查命令/代碼
菱形問號UTF-8 字節流被 ISO-8859-1 解碼檢查請求編碼、數據庫連接編碼
錘子碼 (涓婁竴)GBK 字節流被 UTF-8 解碼檢查頁面編碼聲明、文件讀取編碼
????字符集不支持該字符 (如 latin1 存中文)檢查數據庫/表/列編碼、JDBC URL
正常顯示但長度異常雙重編碼/解碼排查是否多次調用 getBytes/new String

六、經典錯誤代碼修正對照

錯誤寫法正確寫法說明
new String(bytes)new String(bytes, StandardCharsets.UTF_8)無參使用平台默認編碼,不可移植
str.getBytes()str.getBytes(StandardCharsets.UTF_8)同上
new String(str.getBytes("ISO-8859-1"), "GBK")避免雙重轉碼,源頭指定正確編碼除非歷史系統對接,否則重構源頭
request.getParameter() 直接用配置 Filter + Tomcat URIEncoding統一入口解決,業務代碼零侵入

總結

Java 中文亂碼的根治之道是全鏈路 UTF-8 標準化:源碼編碼、編譯編碼、運行時編碼、存儲編碼、傳輸編碼、顯示編碼——六統一。拒絕在業務代碼中手動轉碼,在邊界層 (Filter、連接池、序列化器) 統一攔截處理。遵循「字節流進出必指定編碼,內存中只操作 String」鐵律,亂碼將成為歷史。

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