问题描述

起因是 tomcat 调用servlet输出的System.out.println(也就是所说的控制台输出流)中文乱码,但是其他输出没有受到影响。

问题分析

运行环境:JDK 21, Tomcat 10

我所有的代码都是UTF-8编码的,而且在IDEA中设置了UTF-8编码。

并且Tomcat也配置了-Dfile.encoding=UTF-8选项

通过查看System.out的编码方式,发现是GBK,但是在控制台的编码方式是UTF-8

System.out.println(System.out.charset());   // System.out.charset()方法自jdk18起
// output: GBK

使用jdk17的时候没有问题,但是使用jdk21就会出现乱码问题。

解决方案

参考JEP 400: UTF-8 by Default (openjdk.org)通过调用getProperty方法获取默认字符集

System.out.println("Java Runtime version " + System.getProperty("java.runtime.version"));
System.out.println("----------------------------------------------------------");
//全局默认编码 JDK21是UTF
System.out.println("Charset.defaultCharset()                    = " + Charset.defaultCharset());
//默认文件的编码,这个应该是字节码文件
System.out.println("System.getProperty(\"file.encoding\")       = " + Charset.defaultCharset().displayName()); 
//获取的是本地的字符集编码,中文windows系统应该是GBK
System.out.println("System.getProperty(\"native.encoding\")     = " + System.getProperty("native.encoding"));
System.out.println("System.getProperty(\"sun.jnu.encoding\")    = " + System.getProperty("sun.jnu.encoding"));
//这个是输出流的默认字符集编码
System.out.println("System.getProperty(\"sun.stdout.encoding\") = " + System.getProperty("sun.stdout.encoding"));
//这个是错误流的默认编码
System.out.println("System.getProperty(\"sun.stderr.encoding\") = " + System.getProperty("sun.stderr.encoding"));
//console默认编码
System.out.println("System.console().charset()                  = " + System.console().charset());
//当前输出流的编码
System.out.println("System.out.charset()                        = " + System.out.charset());
System.out.println("----------------------------------------------------------");

在一般的情况下System.getProperty("sun.stdout.encoding")System.getProperty("sun.stderr.encoding")的值是UTF-8,但是在Tomcat中是null

tomcat 10 不会指定输出流的字符编码

所以我们需要在启动tomcat的时候指定输出流的字符编码

是在启动配置中的 VM options 添加参数:

-Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8

这种方法只能解决所设置程序的编码问题,若需解决 Javadoc 的乱码,则需在-D前添加-J。

-J-Dstdout.encoding=UTF-8 -J-Dstderr.encoding=UTF-8

总结

乱码问题只出现在jdk18及以上的版本,因为Java 18 中将默认编码改为了 UTF-8,但没有改动System.out和System.err的编码。

所以使用jdk17没有问题,但是在使用jdk21就会出现乱码问题。

只要在 VM options 中添加 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8 即可设置输出流的编码。

参考


愿此行,终抵群星!