java编码原理 -- 源文件编译和执行

来源:互联网 发布:诽谤方志敏网络文章 编辑:程序博客网 时间:2024/05/03 06:54

编译文件时的编码问题

中文系统默认使用的是gbk编码

    在J:\temp\u8目录先编写GBK格式的T1.java

public class T1 {    public static void main(String[] args){        System.out.println(System.getProperty("file.encoding"));        System.out.println("中");    }}

代码1
    打开cmd窗口进入J:\temp\u8目录,在命令行编译和执行输出如下,文件能够正常编译,并且能正常打印字符串,编译后的class文件大小是4096字节

J:\temp\u8>javac T1.javaJ:\temp\u8>java T1GBK中

    将源文件另存为utf-8格式(建议在myeclipse里面生成这样一个文件copy过去,因为记事本可能会在utf-8文件前面加入文件属性标识的字符),重新执行编译操作,会报如下错误.
这里写图片描述

    为什么utf-8格式的java文件会无法编译而gbk可以呢?

    因为javac命令在没有显示告诉编译器源文件的编码格式时,会根据操作系统的编码方式决定选择的字符集,在中文的win7环境,默认是gbk,所以gbk的源码能够正确编译.

这里写图片描述

    对于utf-8的源文件,[中]编码是[E4 B8 AD].可以通过在ue编辑器打开T1.java源文件查看,当用gbk解析时,[E4 B8]对应[涓],接下来的[AD]由于大于127,gbk会认为这是一个双字节字符,所以会把[AD 22]一起解析,但[AD 22]在gbk中没有对应的字符(见错误”编码GBK的不可映射字符”),所以会显示成一个问号.最终导致后一个双引号被吞掉了,编译器报出了”未结束的字符串文字”;

使用错误的编码格式编译源文件,可能出现乱码

    针对上面分析的两个错误,如果避免以后是否文件就能正常编译了呢?
    我们将代码1中的第4行做如下修改后编译,仍旧报错,但是已经少了双引号被吞的情况

System.out.println(“中 “);

这里写图片描述

    再次修改源文件,编译成功

System.out.println(“中中”);

    运行结果如下

这里写图片描述

    文件能正常编译了,但打印到屏幕的输出变成了红框的内容.
    结合前面的分析,很明显utf-8中的[E4 B8 AD] [E4 B8 AD]两个字符被解析成了gbk的[E4 B8] [AD E4] [B8 AD]三个字符,可以用ue编辑器的编码视图验证. 这里没有出现不可映射的字符,对编译器来说,这是一个合法的文件,所以能够成功编译(但不是正确的编译,因为翻译错误了).乱码,都是对人来说的,即使翻译的意思已经面目全非,对机器来说,只要能映射到字符,都是合法的.

编译时指定的字符集和源码保持一致

    有了以上分析,很明显只要我们告诉编译器源文件的编码类型是utf-8,编译器就能正确编译.如下操作针对代码1的utf-8源文件

这里写图片描述

    可以看到,文件正常编译,而且编译后的class文件大小也是4096字节,并且正常输出.


PS:gbk和utf-8的源文件正确编译后,都是4096字节,说明编译后的class文件和源文件的编码格式没有关系.(java的跨平台性)


java运行时的编码问题

java -D<名称>=<值> 设置JVM运行时的系统属性

    这个有点类似环境变量,比方说用java -Dtest=123 T2 运行如下的java类,控制台会输出123

public class T2 {    public static void main(String[] args){        System.out.println(System.getProperty("test"));    }}

file.encoding 系统属性的作用

    先看一段jdk的源码,这是jdk1.7中java.nio.charset.Charset的部分源码

这里写图片描述

    看方法名就知道是用来获取java默认编码的方法. 这里大概就可以看出,先获取file.encoding的系统属性字符串,然后通过这个字符串去查找是否有对应的字符集,如果找不到,就使用utf-8. 事实上所有涉及到字节和字符转化的地方都会涉及到编码问题,比方java中io,网络传输的地方.而编码问题都涉及到Charset.关于Charset类大家可以看相关的文章,这里只是说明下file.encoding和Charset的关系.

用指定的编码运行java程序

    指定不同的字符集运行之前正确编译的T1,会发现如下结果:

这里写图片描述
得出以下结论:
    1. 中文windows环境默认的GBK编码会传递给java虚拟机(虚拟机启动后会根据操作系统获取字符集),所以file.encoding的默认值是GBK.
    2. file.encoding显示指定的编码会覆盖默认值,且表示编码的字符串不区分大小写
    3. 如果指定的编码不存在,会使用默认的utf-8编码(可以看到指定utf-8和指定xxx输出了同样的内容).

运行时指定utf-8乱码的原因

    上面的结果,后两条命令都输出了乱码. 如果够敏感,看到[涓]这个字就应该意识到是把utf-8的[中]前两个字节用gbk编码去解析产生的结果.

    在cmd窗口用type命令显示T1.java文件的内容(注意文件是utf-8格式).

这里写图片描述

    会发现只有红框的部分没有正确解析,和javac报错显示的一模一样(见图 xx).说明编译器出错的原因和type命令无法正确解析文件的错误是一样一样的.其实现在java打印出来的[涓],也是一个道理.都是因为用gbk字符集去翻译utf-8的字节序列.验证如下:

当前的dos窗口使用的编码格式是gbk

    右击dos窗口标题栏–>属性–>选项, 可以看到当前代码页是gbk.这个的意时是,当前的dos窗口显示文本的时候,是按照gbk编码去解析的.

编辑器读取文件的本质:
    1.文本文件在磁盘上存储的就是一个一个的字节,这个字节是按特定的编码存放的.假设文件a.txt是utf-8编码,里面写了一个[中],那么文件实质存放的是[E4 B8 AD]这三个字节.

    2.文本编辑器将文件内容展示到显示器上的时候,就是用正确的编码方式将字节序列翻译成一个一个的字符(注意并不一定是一对一的关系).如上面的a.txt,如果用utf-8区翻译,那么[E4 B8 AD]这三个字节刚好就还原得到了[中].

    3.如果翻译的时候使用的编码和文件格式不一致,就可能(因为即使不同的编码方式,可能有些字符的编码是一致的)出现乱码.比方说上面的文件,有gbk编码的方式去翻译,前两个字节就会被翻译成[涓].type T1.java之所以只有[中]会变成乱码,就是因为其他的字符utf-8和gbk的编码是一致的,比方说61(换算成10进制是97)用gbk或utf-8去翻译得到的都是字符a,所以不会出现乱码.

    4.java代码

System.out.println(“中”);

    扔给dos窗口(此时他只是一个文本编辑器)的utf-8[中] (因为我们制定了jvm默认使用utf-8编码),和dos窗口从文本中读取的[中]是没有区别的,都是[E4 B8 AD]这个字节序列,而dos窗口按照gbk去解析,所以成了”涓”.

dos窗口显示utf-8字符

    为了证明以上结论,我们可以让dos窗口按utf-8解析文字.

    1.在当前dos窗口执行以下命令

J:\temp\u8>chcp 65001

    2.右击dos窗口标题栏–>属性–>字体,选择Lucida Console字体.
这样我们的dos窗口(此处还是一个文本编辑器),就是按utf-8解析文本文件了

    再次执行type T1.java(utf-8格式),文件将能正常显示,执行java文件,输出如下:

这里写图片描述

    可以看到file.encoding=utf-8能正常显示而gbk不能正常显示.符合之前的分析.(输出的时候中后面多了不可显示字符,我没搞明白原因,如果谁知道还请指教.).

myeclipse都干了什么?

myeclips和编码相关的设置

    设置1:右键项目–>properties–>resource 设置整个项目源文件的编码格式
    设置2:右键文件–>properties–>resource 设置具体文件的编码格式,默认和项目保持一致.

    设置3:右键项目–>run as–>run configurations –>common–>encoding 相当于file.encoding参数设置. 默认和项目编码保持一致(这里要注意,虽然二者保持一致,但实际上是没有任何联系的,因为编译后的文件和源码已经没关系了. 当然,项目中有其他文本文件和配置文件需要在运行时读取另当别论.)
    为什么能够确定设置3就是设置运行时的字符集呢,我们可以在java文件中用

Thread.sleep(100000);

让程序休眠,然后在任务管理器中找到对应的javaw命令进程,命令行(如果没有显示这一列, 查看–>选择列)就能看到执行传递的参数,如下:

这里写图片描述

myeclipse的自动编译功能

    根据上面的设置,我们可以让同一个项目拥有不同编码格式的源文件,很明显myeclipse针对具体的文件,调用javac命令时传了不一样的参数.也进一步证明了源文件的编码方式不影响编译出来的字节码.

myeclipse的控制台

    如果用设置3,尝试不同的编码运行以下项目

public class T2 {    public static void main(String[] args){        System.out.println("我是中文字符串,myeclips认识下呗");    }}

    会发现utf-8,utf-16,gbk都能正常显示,ISO-8859-1和ASCII不能.
    1.ISO-8859-1和ASCII压根就没有编码中文,当然不能显示
    2.myeclipse的控制台比dos窗口智能,能够适应不同字符集的输出(他自己设置的参数,当然知道该用什么字符集解析),好像记事本能够通过推断识别不同编码的文件.

总结

    编译时指定源文件的字符集.

    运行时指定默认的JVM字符集.

    理解myeclipse做了什么.

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 摩托三轮车油刹抱死怎么办 手动档汽车离合抱死怎么办 别克gl8后轮吃胎怎么办 扭力梁后轮吃胎怎么办 非独立悬挂吃胎怎么办 货车半轴法兰盘裂纹怎么办 小天才平板裂屏了怎么办 新车撞了个坑怎么办 新车碰了个坑怎么办 汽车顶被砸了个坑怎么办 途观l前减震异响怎么办 锦明8代声音太大怎么办 手机网页无法加载插件怎么办 微信公众号被投诉怎么办 住了酒店的尾房怎么办 喜欢前任的闺蜜怎么办 闺蜜给介绍对象怎么办 喜欢对象的发小怎么办 山东省直医保卡丢失怎么办 高铁票如果错过了怎么办 动车错过了时间怎么办 长途动车错过了怎么办 动车如果错过了怎么办 没有取票错过了怎么办 动车出站没检票怎么办 火车晚点耽误了下班车怎么办 动车票中途丢了怎么办 购买二手房异地铁路公积金怎么办 沈阳公积金卡丢了怎么办 住宅专项维修资金用完了怎么办 广州出租车丢了东西怎么办 广州的士丢了东西怎么办 网上找兼职被骗了怎么办 海信空调开不了机怎么办 海信空调遥控器开不了怎么办 学生遭套路贷反被仲裁怎么办 赏脸打错字尝脸怎么办 红掌的花变黑了怎么办 红掌花苞发黑了怎么办 水培植物腐根了怎么办 水培绿萝水发臭怎么办