问题描述
起因是 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
即可设置输出流的编码。