您的位置:首页 > 编程语言 > Java开发

编写,编译,运行Java程序过程中的编码解码过程

2015-10-16 21:42 525 查看
在编写,编译,运行Java程序过程中,包含有多个编码解码过程。

一、编写Java程序

某个文本编辑器(比如Intellij Idea内的文本编辑器)编辑Java程序,在将Java源代码片段保存到文件时,需要指定编码方案,文本编辑器会使用该编码方案进行编码,得到字节流,并将其保存到Java文件内。文本编辑器打开一个Java文件,读取文件内的字节流时,首先会去推测应该使用的编码方案,使用该编码方案进行解码,获得字符流。整个编码解码过程如图1所示。

                             图1



二、编译Java程序

编译工具(比如javac)编译Java程序的过程中,首先根据推测得到的编码方案或者通过参数指定(比如对于javac来说,通过-encoding参数来指定要使用的编码方案)的编码方案对Java文件内的字节流进行解码得到字符流,接着对该字符流进行包括语法分析在内的一系列处理,最后使用Class文件规范约定的特定的UTF-8变种进行编码,将得到的字节流保存到Class文件内。整个编码解码过程如图2所示。

                             图2



三、运行Java程序

在运行Java程序的时候,存在好几个编码解码过程,接下来通过具体例子来进行说明。假如现在有如下Java代码:

import java.nio.charset.Charset;

public class Main {
public static void main(String[] args) {
String hello = "你好";
byte[] aa = hello.getBytes(Charset.forName("gbk"));
for (int i = 0; i < aa.length; i++) {
String hex = Integer.toHexString(aa[i] & 0xFF);
if (hex.length() == 1)
hex = '0' + hex;
System.out.println(hex);
}
}
}
在经过“文本编辑,编译”两个步骤之后,得到基于以上Java代码的Class文件。运行该Class文件时,首先读取该Class文件内所包含的字节流,使用Class文件规范约定的特定的UTF-8变种编码方案进行解码,得到字符流(注意该字符流跟以上java代码所示的字符流不一致,因为中间经过了编译过程),该字符流中自然包含了“你好”这个字符流。这个过程如图3所示。

              图3



现在内存中存在着"hello"这个String变量,String变量内部含有char数组变量,而在Java中,一个char其实是由两个字节组成的,即用两个字节的内容来表示一个字符,具体使用的编码方案是UTF-16[1][2]。在以上步骤中,从Class文件中解码得到了“你好”字符流,接下来其实会对“你好”字符流中的每个字符使用UTF-16编码方案进行编码,一个char的字节值存储的就是每个字符使用UTF-16编码方案编码得到的字节值。这个过程如图4所示。

                       图4



在执行到"byte[] aa = hello.getBytes(Charset.forName("gbk"));"语句时,首先获取"hello"变量(对应于char数组)对应的字节序列,使用UTF-16编码方案进行解码,得到“你好”字符流,然后再对该字符流使用GBK编码方案进行编码,得到最终的字节流。这个过程如图5所示:

图5



四、最后是一个综合性的例子

假如有如下Java代码:

public class Main {
public static void main(String[] args) {
System.out.println(Charset.defaultCharset().name());
String hello = "好";
byte[] aa = hello.getBytes(Charset.forName("gbk"));
for (int i = 0; i < aa.length; i++) {
String hex = Integer.toHexString(aa[i] & 0xFF);
if (hex.length() == 1)
hex = '0' + hex;
System.out.println(hex);
}
}
}
建立A项目下的Main.java和B项目下的Main.java两个文件,两个文件各自的编码格式分别为UTF-8和GBK,两个文件的内容都为以上Java代码片段中的内容。获取A下的Main.java和B下的Main.java两个文件的字节流数据,可得两个文件的部分截图分别如图6和图7。

图6



图7



由图6可得,在A下的Main.java中,“好”这个字符对应的字节流为“e5 a5 bd”,符合UTF-8编码方案。由图7可得,在B下的Main.java中,“好”这个字符对应的字节流为“ba c3”,符合GBK编码方案。

接下来分别使用

"javac -encoding utf8 Main.java"

"javac -encoding gbk Main.java"

对A下的Main.java和B下的Main.java进行编译得到A下的Main.class和B下的Main.class

发现这两个.class文件是完全一模一样的。

在执行javac命令的过程中,发生了什么?

1)如上所述,在解码.java文件的时候,需要检测应该使用的编码方案,这里直接使用"-encoding xxx"的命令行参数,指定应该使用的编码方案分别为"utf8"和"gbk"

2)分别根据指定的编码方案解码A下的Main.java和B下的Main.java得到字符流。其中,A下的Main.java解码结果中,"e5 a5 bd"字节流被解码成“好”字符;B下的Main.java解码结果中,"ba c3"字节流被解码成“好”字符。

因而,解码A下的Main.java和B下的Main.java最终得到的两个字符流是完全一致的,内容其实就是“第4节开头处的Java代码片段”。

3)接下来,javac程序分别利用获得的字符流,进行编译生成两者的class文件。由2)知,两者获得的字符流完全一致,而编译生成class文件时采用的编码方案是class文件规范约定的特定的UTF-8变种,那么自然最终生成的两者的class文件也是完全一致的。

查看这两个完全一致的class文件的字节流数据,发现“好”这个字符对应的字节流为“e5 a5 bd”(这个UTF-8变种编码'好'字符得到的结果跟普通的UTF-8编码'好'字符得到的结果一致)

接下来在A下(或者在B下)执行

"java Main"命令

输出结果如图8所示。

图8



在执行java命令的过程中,发生了什么?

1)如上所述,解码class文件的时候,应该使用class文件规范约定的编码方案,即特定的UTF-8的变种

2)在执行class文件中“对应java文件中String hello = '好';”的语句的时候,使用约定的特定的UTF-8的变种,解码class文件中的“e5 a5 bd”字节流,得到"好"这个字符

3)使用UTF-16编码方案对“好”这个字符进行编码,将得到的字节序列值赋值给hello变量中内含的char变量

4)在执行class文件中“对应java文件中byte[] aa = hello.getBytes(Charset.forName('gbk'));”的语句的时候,先对hello变量中内含的char变量的字节值使用UTF-16编码方案进行解码,得到“好”字符,然后再使用gbk编码方案对“好”这个字符进行编码,得到"ba c3"字节流

参考文献:

[1]http://programmers.stackexchange.com/questions/174947/why-does-java-use-utf-16-for-internal-string-representation

[2]http://stackoverflow.com/questions/20966802/utf-16-character-encoding-of-java
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: