Java中字符編碼問(wèn)題的解決方法詳解
前言
在日常 Java 開(kāi)發(fā)中,字符編碼問(wèn)題是一個(gè)非常常見(jiàn)卻又特別容易踩坑的地方。尤其是在不同操作系統(tǒng)之間切換,或者從前端傳到后端、再到數(shù)據(jù)庫(kù),編碼沒(méi)統(tǒng)一好,中文就會(huì)出現(xiàn)“亂碼”。很多同學(xué)第一次遇到的時(shí)候,會(huì)被一大堆奇怪的方塊符號(hào)或者問(wèn)號(hào)整崩潰。
這篇文章就帶你一步一步看清楚字符編碼的來(lái)龍去脈,并結(jié)合可運(yùn)行的代碼,看看如何在 Java 項(xiàng)目里徹底解決編碼不一致的問(wèn)題。
背景:為什么會(huì)出現(xiàn)編碼問(wèn)題
其實(shí)原因很簡(jiǎn)單:不同系統(tǒng)、不同軟件的默認(rèn)字符編碼不一樣。
- Windows 上默認(rèn)編碼是 GBK 或 CP936。
- Linux、Mac 大部分是 UTF-8。
- 數(shù)據(jù)庫(kù)可能是 Latin1、GBK 或 UTF-8。
- Tomcat、IDEA 默認(rèn)也可能不是 UTF-8。
舉個(gè)例子,如果你的 Java 程序里寫(xiě)了一行中文字符串 "你好",在 UTF-8 下存儲(chǔ)沒(méi)問(wèn)題,但如果有人用 GBK 來(lái)讀取,就會(huì)直接炸掉,變成“亂碼”。
常見(jiàn)場(chǎng)景分析
控制臺(tái)輸出亂碼
在 Windows 的 CMD 下運(yùn)行 Java 程序時(shí),經(jīng)常會(huì)看到控制臺(tái)打印中文是亂碼。這是因?yàn)?Windows 控制臺(tái)默認(rèn)用 GBK 編碼,但你的 Java 程序里可能用的是 UTF-8。
public class EncodingDemo {
public static void main(String[] args) {
String msg = "你好,世界";
System.out.println(msg);
}
}
在 Linux/Mac 控制臺(tái)上運(yùn)行,大概率沒(méi)問(wèn)題。但在 Windows CMD 里,就會(huì)看到一堆奇怪符號(hào)。
文件讀寫(xiě)亂碼
當(dāng)你從文件里讀中文內(nèi)容時(shí),如果讀的時(shí)候用的編碼和寫(xiě)的時(shí)候不一樣,也會(huì)直接出錯(cuò)。
import java.io.*;
public class FileEncodingDemo {
public static void main(String[] args) throws Exception {
String text = "中文內(nèi)容測(cè)試";
// 寫(xiě)入文件,強(qiáng)制使用 UTF-8
try (Writer writer = new OutputStreamWriter(new FileOutputStream("test.txt"), "UTF-8")) {
writer.write(text);
}
// 讀取文件(錯(cuò)誤示范:不指定編碼)
try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) {
System.out.println("讀到的內(nèi)容:" + reader.readLine());
}
// 正確方式:指定 UTF-8
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt"), "UTF-8"))) {
System.out.println("正確讀到的內(nèi)容:" + reader.readLine());
}
}
}
運(yùn)行后你會(huì)發(fā)現(xiàn),沒(méi)指定編碼時(shí)中文是亂碼,指定了 UTF-8 之后就正常了。
數(shù)據(jù)庫(kù)存取亂碼
數(shù)據(jù)庫(kù)也是高頻出錯(cuò)點(diǎn),比如 MySQL 默認(rèn)的 latin1 編碼就很坑。假設(shè)表結(jié)構(gòu)是這樣的:
CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) ) DEFAULT CHARSET=latin1;
如果你在 Java 里用 UTF-8 往里面寫(xiě)入 "張三",再讀出來(lái)時(shí)就會(huì)發(fā)現(xiàn)已經(jīng)是亂碼。
解決辦法是:
建庫(kù)建表時(shí)就指定 utf8mb4:
CREATE DATABASE demo DEFAULT CHARSET=utf8mb4;
JDBC 連接時(shí)也要加上編碼參數(shù):
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
解決方案
那我們?cè)撛趺唇y(tǒng)一解決這個(gè)問(wèn)題呢?其實(shí)有幾個(gè)常見(jiàn)思路:
統(tǒng)一使用 UTF-8
UTF-8 是現(xiàn)在最通用的編碼方式,跨系統(tǒng)兼容性最好。所以最穩(wěn)妥的做法就是:整個(gè)鏈路都統(tǒng)一成 UTF-8。
包括:源代碼文件、編譯參數(shù)、運(yùn)行參數(shù)、數(shù)據(jù)庫(kù)配置、Tomcat 配置。
比如在 Maven 項(xiàng)目里,你可以在 pom.xml 里強(qiáng)制指定源碼編碼:
<project>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
這樣即便在 Windows 上編譯,結(jié)果也不會(huì)變。
設(shè)置 JVM 參數(shù)
如果你發(fā)現(xiàn)運(yùn)行環(huán)境默認(rèn)編碼不是 UTF-8,可以在 JVM 啟動(dòng)時(shí)加上參數(shù):
java -Dfile.encoding=UTF-8 -jar app.jar
這會(huì)讓整個(gè) Java 虛擬機(jī)的默認(rèn)編碼改成 UTF-8,很多情況下能一勞永逸。
數(shù)據(jù)庫(kù)設(shè)置字符集
在 MySQL 里,推薦直接用 utf8mb4,這樣連 emoji 表情都能存:
ALTER DATABASE demo CHARACTER SET utf8mb4; ALTER TABLE user CONVERT TO CHARACTER SET utf8mb4;
同時(shí),Java 里的 JDBC 連接也要顯式指定編碼,否則還是會(huì)出問(wèn)題。
實(shí)際案例:亂碼排查經(jīng)驗(yàn)
我自己就踩過(guò)一個(gè)坑:在 Windows 下本地開(kāi)發(fā),數(shù)據(jù)庫(kù)是 utf8mb4,項(xiàng)目里也設(shè)了 -Dfile.encoding=UTF-8,一切正常。但是代碼上線(xiàn)到 Linux 服務(wù)器后,日志里的中文全是亂碼。排查了半天,最后發(fā)現(xiàn)是 日志框架的配置文件沒(méi)聲明 UTF-8,導(dǎo)致寫(xiě)日志文件時(shí)被當(dāng)成系統(tǒng)默認(rèn)編碼。
后來(lái)改了一行配置就好了:
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<charset>UTF-8</charset>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
所以要點(diǎn)就是:不要依賴(lài)默認(rèn)值,凡是涉及到字符集的地方都要顯式聲明 UTF-8。
總結(jié)
Java 的字符編碼問(wèn)題,說(shuō)白了就是“讀和寫(xiě)不一致”。解決它的核心就是統(tǒng)一,特別是統(tǒng)一用 UTF-8。
- 源代碼、編譯、運(yùn)行 JVM 都統(tǒng)一 UTF-8。
- 文件讀寫(xiě)時(shí)顯式指定編碼。
- 數(shù)據(jù)庫(kù)用
utf8mb4并在 JDBC 連接里加上參數(shù)。
只要做到這幾點(diǎn),基本就不會(huì)再遇到莫名其妙的亂碼問(wèn)題。
到此這篇關(guān)于Java中字符編碼問(wèn)題的解決方法詳解的文章就介紹到這了,更多相關(guān)Java字符編碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis-Plus多數(shù)據(jù)源的示例代碼
本文主要介紹了MyBatis-Plus多數(shù)據(jù)源的示例代碼,包括依賴(lài)配置、數(shù)據(jù)源配置、Mapper 和 Service 的定義,具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05
mybatis快速入門(mén)學(xué)習(xí)教程新手注意問(wèn)題小結(jié)
MyBatis 是支持定制化 SQL、存儲(chǔ)過(guò)程以及高級(jí)映射的優(yōu)秀的持久層框架。接下來(lái)通過(guò)本文給大家介紹mybatis快速入門(mén)學(xué)習(xí)教程新手注意問(wèn)題小結(jié),需要的朋友可以參考下2017-02-02
SpringCloud創(chuàng)建多模塊項(xiàng)目的實(shí)現(xiàn)示例
,Spring Cloud作為一個(gè)強(qiáng)大的微服務(wù)框架,提供了豐富的功能和組件,本文主要介紹了SpringCloud創(chuàng)建多模塊項(xiàng)目的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下2024-02-02
MyBatis XML 配置文件之從配置規(guī)范到 CRUD 開(kāi)發(fā)實(shí)踐記錄
本文介紹了MyBatis的XML配置文件的使用,包括數(shù)據(jù)庫(kù)連接配置、MyBatis配置、XML文件的核心組成成分以及增刪查改操作的示例,感興趣的朋友跟隨小編一起看看吧2026-01-01
Java實(shí)現(xiàn)Windows計(jì)算器界面
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)Windows計(jì)算器界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
Spring?Data?JPA命名約定查詢(xún)實(shí)現(xiàn)方法
這篇文章主要為大家介紹了Spring?Data?JPA命名約定查詢(xún)實(shí)現(xiàn)方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12

