Java中文乱码问题

推荐

ANSI - 百度百科

字符编码笔记:ASCII,Unicode 和 UTF-8

Java中文乱码解决之道

从外部编码的角度再议 Java 乱码问题

实验一

目的:源文件编码方式、javac编译参数encoding 二者的关系?

先阅读文章 java中文乱码解决之道(四)——java编码转换过程Java源文件到字节码等情况的编码转换

情况 源文件编码 javac读取源文件编码 javac编译结果
含中文 UTF-8 GBK 失败
含中文 ANSI GBK 成功
含中文 ANSI UTF-8 失败
纯英文 任意 任意 成功

GBK 汉字占两个字节;UTF-8 汉字大多占三个字节,有些占4个字节。

1
2
3
4
javac ANSI.java /* 默认使用Windows默认的GBK编码 */
javac UTF8.java /* 默认使用Windows默认的GBK编码 */
javac -encoding GBK ANSI.java /* 指定读取源文件编码GBK */
javac -encoding UTF-8 UTF8.java /* 指定读取源文件编码UTF-8 */

实验二

目的:找出导致cmd控制台乱码的因素,源文件编码、class文件编码、jvm编码、控制台编码。

无论源文件使用什么编码方式存储,无论内容是中文字符还是英文字符,编译后的class文件内容都是Unicode编码。所以源文件编码和class文件编码不会影响cmd控制台乱码。

1
2
3
4
5
# 改变控制台编码
chcp 65001 # UTF-8
chcp 936 # GBK
# 指定jvm编码;若未指定就使用Windows默认的GBK编码。
java -jar -Dfile.encoding=UTF-8 service-sys-start.jar

System.out.println乱码

将下面内容分别另存为 ANSI.javaUTF8.java文件名=类名=文件编码

1
2
3
4
5
6
7
8
9
10
11
12
public class ClassName {
public static void main(String[] args) {
System.out.println("当前JRE版本:" + System.getProperty("java.version"));
// 查看JVM运行时所使用的编码。Windows默认file.encoding=”GBK”;Linux默认file.encoding=”UTF-8”。
// System.out.println("当前JVM的默认字符集:" + Charset.defaultCharset());
System.out.println("当前JVM的默认字符集:" + System.getProperty("file.encoding"));

System.out.println("java后面跟着的是java文件的类名,后面不要加.class。");
System.out.println("java后面跟着的是java文件的类名,后面不要加.class。");
System.out.println("java后面跟着的是java文件的类名,后面不要加.class。");
}
}

运行:

1
2
3
4
5
6
7
8
9
10
11
chcp 936
java ANSI # 正常
java UTF8 # 正常
java -Dfile.encoding=UTF-8 ANSI # 正常
java -Dfile.encoding=UTF-8 UTF8 # 正常

chcp 65001
java ANSI # 乱码
java UTF8 # 乱码
java -Dfile.encoding=UTF-8 ANSI # 正常
java -Dfile.encoding=UTF-8 UTF8 # 正常

结论:

控制台编码 jvm编码 是否乱码
GBK GBK
GBK UTF-8
UTF-8 UTF-8
UTF-8 GBK

log4j2打印控制台乱码

日志配置文件中已经指定了console输出源是UTF-8编码,所以保证控制台是UTF-8即可(chcp 65001)。

启动脚本

1
2
3
4
5
6
7
8
chcp 65001
set JAVA_HOME=D:\Program Files\Java64\jdk1.8.0_192
set PATH=%JAVA_HOME%\bin;
set JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8
:: 开启远程调试
set JAVA_TOOL_OPTIONS=%JAVA_TOOL_OPTIONS% -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005
java -jar -Dserver.port=60001 service-sys-start.jar
pause;

各种OPTIONS的区别

也许你经常遇到 JAVA_OPTS_JAVA_OPTIONSJAVA_TOOL_OPTIONS ,那么他们有什么不同呢?

  • JAVA_OPTS:常用于一些应用的配置,如Tomcat,但它一般不作为环境变量,也不能被JVM识别的,是那些应用的自定义配置;
  • _JAVA_OPTIONS:也是作为环境变量来替代命令行参数的,但它是 JVM 厂家自定义的,可以覆盖 JAVA_TOOL_OPTIONS,但各厂家的不同,_JAVA_OPTIONS 是 Oracle 的 JVM,而 IBM 的则是用 IBM_JAVA_OPTIONS
  • JAVA_TOOL_OPTIONS:是标准的,所有虚拟机都能识别和应用的。

如果想验证上面的不同也不难,如果设置了 JVM 能识别的环境变量,JVM会有”Picked up”的提示的,如:

1
2
3
4
5
6
7
8
9
10
11
:: 能被JVM识别
set JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8
java -version

:: 能被JVM识别
set _JAVA_OPTIONS=-Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8
java -version

:: 不能被JVM识别
set JAVA_OPTS=-Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8
java -version

可见 _JAVA_OPTIONS 覆盖了 JAVA_TOOL_OPTIONS

JAVA_TOOL_OPTIONS 用于解决的经典问题是使用命令行导致中文乱码