Java 编码 (二)

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属性


Edit Configurations.png

此时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类,产生如下的报错信息:


javac编译报错信息.png

出现这个报错信息,是因为我的JavaEncode.java文件以GBK编码的形式存储在磁盘中,“中国”两个汉字对应的十六进制为【D6D0 B9FA】。而javac指令在编译这个类的时候,是以操作系统默认的UTF-8编码格式,来解析这个类,“中国”两个字的字节数组无法解析为UTF-8编码的字符,导致报错。

这时可以使用如下指令编译这个类

javac  -encoding gbk JavaEncode.java

我们使用IDEA做开发工具的时候,发现这个Java类可以正常编译运行。查看一下IDEA所使用的编译器:


image.png

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 /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."":()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."":([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               
  #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        // "":()V
  #35 = Utf8               中国
  #36 = Utf8               gbk
  #37 = NameAndType        #50:#51        // getBytes:(Ljava/lang/String;)[B
  #38 = Utf8               java/lang/String
  #39 = NameAndType        #13:#52        // "":([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工具类来源于

 
        cn.hutool
        hutool-all
        5.2.5

你可能感兴趣的:(Java 编码 (二))