JDK 18 及以上使用標準輸出流中文輸出亂碼問題

talentestors發表於2024-12-08

著作權歸作者所有。
商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
連結:https://stazxr.cn/2024/12/05/JDK-18-以上使用標準輸出流中文輸出亂碼問題/
來源:終わり群星

問題描述

起因是 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 即可設定輸出流的編碼。

參考

  • JEP 400: UTF-8 by Default (openjdk.org)
  • https://openjdk.java.net/jeps/400
  • 解決 Java 18 以上 IDEA 中文輸出亂碼問題(知乎)
  • 解決IDEA控制檯中輸出中文亂碼並探究原因(JDK18及以上)

寫在最後

End

相關文章