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 <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>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335