Java 实战 - 字符编码问题解决方案
最近在做一个 Java 项目的时候,遇到了一个让人头疼的问题:在 Windows 上开发的时候,中文字符显示正常,但部署到 Linux 服务器上就变成乱码了。刚开始以为是数据库的问题,检查了数据库字符集也没问题。后来才发现,原来是不同系统的默认编码不一致导致的。 相信很多开发者都遇到过类似的问题:代码里写的中文,在不同环境下显示不一样;从数据库读取的数据,有时候是乱码;日志文件里的中文显示不正常。这些问题虽然看起来简单,但如果不了解字符编码的原理,可能会折腾很久。今天我们就来聊聊字符编码问题的原因和解决方案。 字符编码问题是一个很常见但又容易被忽视的问题。不同的系统、不同的环境,默认的字符编码可能不一样,这就导致了各种乱码问题。 现在的计算机系统,底层都是使用二进制来存储数据的。但我们在屏幕上看到的文字,比如中文、英文、日文等,都是字符。要把字符转换成二进制存储,就需要用到字符编码。 常见的字符编码: 问题根源: 不同系统默认的字符编码不一样: 如果你的代码在 Windows 上开发(默认 GBK),但部署到 Linux 服务器上(默认 UTF-8),就可能出现乱码问题。 在实际开发中,我们经常会遇到这些乱码场景: 场景一:文件读取乱码 从文件读取数据时,如果文件的编码和读取时使用的编码不一致,就会出现乱码。比如文件是 UTF-8 编码的,但用 GBK 编码去读取,中文就会变成乱码。 场景二:数据库存储乱码 数据库的字符集设置不对,或者连接数据库时没有指定正确的字符集,存储和读取的数据就可能出现乱码。 场景三:日志输出乱码 程序输出的日志,如果控制台的编码设置不对,或者日志文件的编码不对,中文日志就会显示成乱码。 场景四:网络传输乱码 不同系统之间通过网络传输数据时,如果编码不一致,接收到的数据可能就是乱码。 解决字符编码问题最根本的方法,就是统一使用 UTF-8 编码。UTF-8 是目前最流行的字符编码,几乎所有的现代系统都支持,而且可以表示世界上所有的字符。 UTF-8 有很多优点: 代码文件编码: 确保所有的源代码文件都使用 UTF-8 编码保存。在 IDE 中,可以设置默认的文件编码: 配置文件编码: 配置文件(如 properties、xml、json 等)也要使用 UTF-8 编码。特别是 properties 文件,如果包含中文,必须使用 UTF-8 编码,否则会出现乱码。 资源文件编码: 资源文件(如国际化文件、模板文件等)也要使用 UTF-8 编码。 在 Java 项目中,JVM 的默认字符编码可能会影响程序的运行。如果 JVM 的默认编码不是 UTF-8,可能会导致文件读写、网络传输等操作出现乱码。 在启动 Java 程序时,可以通过 JVM 参数来设置字符编码: 这个参数会告诉 JVM,文件系统的默认编码是 UTF-8。 如果你在 IDE 中运行程序,也需要设置 JVM 参数: IntelliJ IDEA: Eclipse: 除了 JVM 参数,也可以在代码中设置系统属性: 但这种方式不推荐,因为有些代码可能在设置之前就已经读取了系统属性。 可以通过代码来验证当前的编码设置: 如果输出不是 UTF-8,说明设置没有生效。 数据库的字符集设置也很重要,如果数据库的字符集不对,存储和读取的数据就可能出现乱码。 创建数据库时设置字符集: 创建表时设置字符集: 修改现有数据库字符集: 如果数据库已经创建,可以修改字符集: 在连接数据库时,也要指定字符集: JDBC 连接字符串: 或者使用更完整的参数: Spring Boot 配置: 在 或者: PostgreSQL: PostgreSQL 默认使用 UTF-8 编码,创建数据库时: Oracle: Oracle 数据库的字符集在创建数据库时设置,之后很难修改。建议在创建数据库时就选择 UTF-8 相关的字符集,如 让我们看看几个实际应用场景,了解如何在实际项目中应用这些解决方案: 在 Web 应用中,需要处理前端的请求和响应: 设置请求和响应编码: 在 Spring MVC 中,可以配置字符编码过滤器: 或者在 在读写文件时,要明确指定编码: 读取文件: 写入文件: 在输出日志时,要确保日志文件的编码正确: Logback 配置: Log4j2 配置: 在实际项目中,为了避免字符编码问题,建议遵循以下最佳实践: 在代码中,凡是涉及字符编码的地方,都要明确指定 UTF-8,不要依赖系统默认编码: 在项目启动时,可以输出当前的编码设置,方便排查问题: 在部署到不同环境之前,要测试字符编码是否正常: 确保在所有环境下,中文字符都能正常显示。 字符编码问题虽然看起来简单,但在实际项目中却经常遇到。解决这类问题的关键是统一使用 UTF-8 编码,并在所有可能涉及编码的地方明确指定。 关键点总结: 最佳实践:前言
问题背景
为什么会出现编码问题
常见的乱码场景
解决方案一:统一使用 UTF-8
为什么选择 UTF-8
如何在项目中统一使用 UTF-8
解决方案二:设置 JVM 参数
设置 JVM 参数
java -Dfile.encoding=UTF-8 -jar your-app.jar在 IDE 中设置
-Dfile.encoding=UTF-8-Dfile.encoding=UTF-8在代码中设置
System.setProperty("file.encoding", "UTF-8");验证编码设置
System.out.println("默认字符编码: " + System.getProperty("file.encoding"));
System.out.println("控制台编码: " + System.getProperty("console.encoding"));解决方案三:数据库字符集设置
MySQL 字符集设置
CREATE DATABASE mydb
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;utf8mb4 是 MySQL 中真正的 UTF-8 编码,可以存储所有的 Unicode 字符,包括 emoji 表情。而 MySQL 的 utf8 实际上只支持最多 3 字节的字符,不支持 emoji。CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;ALTER DATABASE mydb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE users CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;连接数据库时设置字符集
String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8&useSSL=false";String url = "jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=UTC";application.properties 或 application.yml 中配置:spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8mb4&useSSL=false
spring.datasource.username=root
spring.datasource.password=passwordspring:
datasource:
url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8mb4&useSSL=false
username: root
password: password其他数据库的字符集设置
CREATE DATABASE mydb WITH ENCODING 'UTF8';AL32UTF8。实际应用场景
场景一:Web 应用开发
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return filter;
}
}web.xml 中配置:<filter>
<filter-name>characterEncodingFilter</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>
<param-value>true</param-value>
</init-param>
</filter>场景二:文件读写
// 使用 UTF-8 编码读取文件
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}// 使用 UTF-8 编码写入文件
try (BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("file.txt"), StandardCharsets.UTF_8))) {
writer.write("中文内容");
}场景三:日志输出
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>app.log</file>
<encoder>
<charset>UTF-8</charset>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
</configuration><Configuration>
<Appenders>
<File name="File" fileName="app.log">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n" charset="UTF-8"/>
</File>
</Appenders>
</Configuration>最佳实践建议
统一编码规范
明确指定编码
// 好的做法:明确指定编码
String content = new String(bytes, StandardCharsets.UTF_8);
// 不好的做法:依赖默认编码
String content = new String(bytes); // 可能在不同环境下表现不一致验证编码设置
@PostConstruct
public void checkEncoding() {
System.out.println("file.encoding: " + System.getProperty("file.encoding"));
System.out.println("Default Charset: " + Charset.defaultCharset());
}测试不同环境
总结
-Dfile.encoding=UTF-8 设置 JVM 默认编码





