Java18的一个特性引发的IDEA控制台中文乱码的问题

问题描述

今天用到JDK 18,遇到中文在IDEA控制台输出是乱码,但是文件输出是正常的问题,经过排查,确定是在IDEA 2021.3.3下使用JDK 18打印到IDEA控制台才会出现的bug。将项目编译打包,在cmd等命令行窗口中控制台使出的中文是正常的。

解决方法

  1. 切换项目默认文件字符集为GBK
  2. 启动行添加VM参数:
-Dfile.encoding=COMPAT

复现情况

今天写学demo时用到了JDK 18,结果发现控制台输出的中文有乱码,如

System.out.println(你好 JeremyTsai);

打印出来的效果如下:

��� JeremyTsai

里面的你好乱码了。一开始还以为是JDK的bug,但是尝试打包出来,在cmd运行,又是正常的。于是乎怀疑是IDEA控制台的bug。故写了以下代码测试:

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;

/**
 * @author JeremyTsai
 * @version 0
 * @since 2022/6/2 00:10
 */
public class App {

    public static FileOutputStream out;

    static {
        try {
            out = new FileOutputStream("log.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        print("Java版本为: " + System.getProperty("java.version"));
        print("文件编码为: " + System.getProperty("file.encoding"));
        print("本地编码为: " + System.getProperty("native.encoding"));
        print("你好 JeremyTsai");
        out.close();
    }

    private static void print(String str) throws Exception {
        System.out.println(str);
        out.write(str.getBytes(StandardCharsets.UTF_8));
        out.write("\\r\\n".getBytes(StandardCharsets.UTF_8));
        out.flush();
    }

}

上述代码分别测试IDEA控制台打印和文件输出打印。
测试结果如下:
JDK17 IDEA控制台输出正常,文件输出正常。

Java版本为: 17
文件编码为: UTF-8
本地编码为: GBK
你好 JeremyTsai

JDK18 IDEA控制台乱码,文件输出正常。

Java�汾Ϊ: 1
�ļ�����Ϊ: UTF-8
���ر���Ϊ: GBK
��� JeremyTsai

问题研究

值得关注的一点就是,native encoding都是GBK,这是因为我本地是中文的windows系统。这里会乱码,也是因为JDK18的新特性:JEP400: 默认字符集UTF-8导致的,猜想可能是因为IDEA的控制台的编解码有问题。所以解决的方案就有很多,因为本地是中文系统,最简单的就是将项目字符集转成GBK,这样file.encoding和native encoding都是GBK,不可能存在编解码问题。还有一个解决方案就是在UTF-8字符集的项目启动时加VM参数

-Dfile.encoding=COMPAT

也可以解决。在JDK 17引入了native.encoding系统属性,作为程序获得JDK算法所选字符集的标准方式,而不管默认字符集是否实际配置为该字符集。在 JDK 18中,如果在命令行上将file.encoding设置为COMPAT,那么file.encoding的运行时值将与native.encoding的运行时值相同; 如果在命令行上将file.encoding设置为 UTF-8,那么 file.encoding 的运行时值可能与native.encoding的运行时值不同。

JEP400 默认字符集UTF-8

摘要

指定 utf-8作为标准 Java APIs 的默认字符集。通过这一更改,依赖于默认字符集的 api 将在所有实现、操作系统、区域设置和配置中表现一致。

目标

  1. 当Java程序的代码依赖于默认字符集时,使它们更可预测和可移植
  2. 明确标准Java API在任何地方使用默认字符集
  3. 除控制台 I/O 之外,标准化所有标准Java API中的 UTF-8

引用


封面

梦华录插画风


Java18的一个特性引发的IDEA控制台中文乱码的问题
https://wangijun.com/2022/06/26/java-16/
作者
无良芳
发布于
2022年6月26日
许可协议