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做了什么.
- java编码原理 -- 源文件编译和执行
- Java源文件的编译、下载、解释和执行
- Java源文件的编译、下载、解释和执行
- Java源文件的编译、下载、解释和执行
- Maven中指定Java的编译版本和源文件编码方式
- java源文件和class文件编码详解
- 批处理批量编译java源文件,并有条件的自动执行
- PHP编译执行Java源文件并重定向输出
- java源文件编码问题
- java编译和执行
- 动态编译Java源文件
- 编译Java源文件
- javac编译java源文件
- JAVA源文件动态编译
- 【编译原理之】Bison 源文件结构原理
- JAVA源文件编码问题总结
- JAVA-编译-包-将源文件和类文件分开
- javac 编译源文件时指定编码格式
- Xcode7--免证书真机调试步骤
- 读书笔记—View的事件体系(1)
- 在Ubuntu14.04上快速部署OpenStack
- jvm的内存构成
- Redis入门(一)
- java编码原理 -- 源文件编译和执行
- Codeforces India Hack2016 653ABCED
- 最新版 CocoaPods 的安装步骤
- 自定义ANDROID中EDITTEXT中的HINT文本的大小
- TextView动态设置字体
- zoj 3846 GCD Reduce
- ViewDragHelper详解
- 大话模式学习笔记-代码无错就是优?-简单工厂模式
- IOS学习之——关闭ios虚拟键盘的几种方法