JVM默认file.encoding
Mac和Linux里面JVM默认的文件编码格式是UTF-8,Windows电脑据说默认的是GBK编码,大家可以通过这种方式来查询JVM的文件编码方式
System.getProperty("file.encoding")
我现在使用的是Mac,默认file.encoding为UTF-8编码,下面所有测试结果为Mac上面的实验数据。
看下面这段代码
String str = "中国";
byte[] gbkBytes = str.getBytes("gbk");
String gbkStr = new String(gbkBytes, "gbk");
System.out.println("将字节数组转化为16进制");
System.out.println(HexUtil.encodeHex(gbkStr.getBytes()));
大家猜想一下这个时候str和gbkStr里面的 char[]数组存储的是什么。经过debug发现,两个char[]数组长度都为2,存储的都是20013和22269(十进制),转化为十六进制之后为4E2D 和 56FD,正好是“中国“两个汉字的Unicode字符代码。
上面代码输出的十六进制字符串为:E4B8AD E59BBD,恰好是中国两个汉字的UTF-8编码。
修改JVM file.encoding
IDEA启动的时候,可以在这个位置配置JVM的file.encoding属性
此时str和gbkStr里面的 char[]数组存储的是什么呢?经过debug发现,两个char[]数组长度为2,存储的为20013和22269(十进制),转化为十六进制之后为4E2D 和 56FD,正好是“中国“两个汉字的Unicode字符代码。
此时上面代码输出的结果为:D6D0 B9FA,是中国两个汉字的GBK编码。
为什么会出现这种情况?
从上面可以看出, 不论 file.encoding被设置成什么,JVM内存表示字符的时候,采用的都是Unicode字符代码(UCS-2),也即UTF16编码。
观察String类getBytes()方法的源码
String csn = Charset.defaultCharset().name();
返回值csn为我们通过-Dfile.encoding参数设置的编码。String类的getBytes()方法,默认使用Charset.defaultCharset()返回的字符编码对字符串进行编码,所以通过getBytes()方法获取汉字的字节数组是什么由-Dfile.encoding设置的编码决定,所以-Dfile.encoding=UTF-8时,字节数组为E4B8AD E59BBD。-Dfile.encoding=GBK时,字节数组为D6D0 B9FA。
所以-Dfile.encoding编码仅对获取字符串的字节流起作用(不指定编码的情况下),在 new String(byte[], charset)的时候,不管指定的编码是什么,字符串在虚拟机内存中都以Unicode字符集表示的。
javac编译
现在有一个java类,我将它的文件编码修改为GBK:
public class JavaEncode {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "中国";
byte[] gbkBytes = str.getBytes("gbk");
String gbkStr = new String(gbkBytes, "gbk");
System.out.println(HexUtil.encodeHex(gbkStr.getBytes()));
}
}
然后使用javac指令直接编译这个java类,产生如下的报错信息:
出现这个报错信息,是因为我的JavaEncode.java文件以GBK编码的形式存储在磁盘中,“中国”两个汉字对应的十六进制为【D6D0 B9FA】。而javac指令在编译这个类的时候,是以操作系统默认的UTF-8编码格式,来解析这个类,“中国”两个字的字节数组无法解析为UTF-8编码的字符,导致报错。
这时可以使用如下指令编译这个类
javac -encoding gbk JavaEncode.java
我们使用IDEA做开发工具的时候,发现这个Java类可以正常编译运行。查看一下IDEA所使用的编译器:
IDEA默认使用的编译器是Javac(eclipse有自己的编译器,有兴趣的自行查阅)。IDEA使用的Javac和JDK中自带的Javac是同一个东西吗?
[https://youtrack.jetbrains.com/issue/IDEA-72010](https://youtrack.jetbrains.com/issue/IDEA-72010)
Since JDK 6 the javac compiler no longer depends directly on rt.jar to perform it's compilation,
but it depends on a stripped version of the rt.jar present in <JDK>/lib/ct.sym. IntelliJ however
directly uses rt.jar for compilation and error highlighting.
所以这两个还是有区别的。
不管这个区别,IDEA在对类进行编译的时候,应该会根据类文件的编码,进行相应的转换。
我们使用javap -verbose指令观察这个类编译之后的字节码
Constant pool:
#1 = Methodref #12.#34 // java/lang/Object."<init>":()V
#2 = String #35 // 中国
#3 = String #36 // gbk
#4 = Methodref #5.#37 // java/lang/String.getBytes:(Ljava/lang/String;)[B
#5 = Class #38 // java/lang/String
#6 = Methodref #5.#39 // java/lang/String."<init>":([BLjava/lang/String;)V
#7 = Fieldref #40.#41 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Methodref #5.#42 // java/lang/String.getBytes:()[B
#9 = Methodref #43.#44 // cn/hutool/core/util/HexUtil.encodeHex:([B)[C
#10 = Methodref #45.#46 // java/io/PrintStream.println:([C)V
#11 = Class #47 // com/induschain/encode/JavaEncode
#12 = Class #48 // java/lang/Object
#13 = Utf8 <init>
#14 = Utf8 ()V
#15 = Utf8 Code
#16 = Utf8 LineNumberTable
#17 = Utf8 LocalVariableTable
#18 = Utf8 this
#19 = Utf8 Lcom/induschain/encode/JavaEncode;
#20 = Utf8 main
#21 = Utf8 ([Ljava/lang/String;)V
#22 = Utf8 args
#23 = Utf8 [Ljava/lang/String;
#24 = Utf8 str
#25 = Utf8 Ljava/lang/String;
#26 = Utf8 gbkBytes
#27 = Utf8 [B
#28 = Utf8 gbkStr
#29 = Utf8 Exceptions
#30 = Class #49 // java/io/UnsupportedEncodingException
#31 = Utf8 MethodParameters
#32 = Utf8 SourceFile
#33 = Utf8 JavaEncode.java
#34 = NameAndType #13:#14 // "<init>":()V
#35 = Utf8 中国
#36 = Utf8 gbk
#37 = NameAndType #50:#51 // getBytes:(Ljava/lang/String;)[B
#38 = Utf8 java/lang/String
#39 = NameAndType #13:#52 // "<init>":([BLjava/lang/String;)V
#40 = Class #53 // java/lang/System
#41 = NameAndType #54:#55 // out:Ljava/io/PrintStream;
#42 = NameAndType #50:#56 // getBytes:()[B
#43 = Class #57 // cn/hutool/core/util/HexUtil
#44 = NameAndType #58:#59 // encodeHex:([B)[C
#45 = Class #60 // java/io/PrintStream
#46 = NameAndType #61:#62 // println:([C)V
#47 = Utf8 com/induschain/encode/JavaEncode
#48 = Utf8 java/lang/Object
#49 = Utf8 java/io/UnsupportedEncodingException
#50 = Utf8 getBytes
#51 = Utf8 (Ljava/lang/String;)[B
#52 = Utf8 ([BLjava/lang/String;)V
#53 = Utf8 java/lang/System
#54 = Utf8 out
#55 = Utf8 Ljava/io/PrintStream;
#56 = Utf8 ()[B
#57 = Utf8 cn/hutool/core/util/HexUtil
#58 = Utf8 encodeHex
#59 = Utf8 ([B)[C
#60 = Utf8 java/io/PrintStream
#61 = Utf8 println
#62 = Utf8 ([C)V
可以看到编译产生的.class文件采用的都是UTF-8编码,不管文件是以什么形式存储的。
文中的HexUtil工具类来源于
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.2.5</version>
</dependency>