JVM在设计中是不允许多种编码方式并存的。这是因为如果在内存中Java字符可以有GBK、UTF-16等多种编码形式存在,那么对开发者来说,连打印字符串都寸步难行。例如,一个GB2312字符串后面跟了一个UTF-8字符串,那么连接后的最终结果是什么编码呢?
Java开发者必须牢记:在Java中,字符串只以一种形式存在,那就是Unicode(不选择任何特定的编码,直接使用他们在字符集中的编号,这是统一的唯一方法)。
但在Java中到底是指哪里呢?是指在JVM中,内存中,代码中声明的每一个char、String类型变量中。例如,在程序中写:
char han=‘永‘;
在内存的相应区域,这个字符就表示为0x6c38,可以用下面的代码证明:
char han=‘永‘;
System.out.format("%x",(short)han);
输出是6c38.反过来使用Unicode编号来制定一个字符也可以,像这样:
char han=0x6c38;
System.out.println(han);
输出是:永。
这其实也是说,只要你正确读入了“永”字,那么它在内存中的表示形式一定是0x6c38,没有任何其他值会代表这个字。
JVM的这种约定使得一个字符分为两部分:JVM内部和OS的文件系统。在JVM内部,统一使用Unicode表示,当这个字符被从JVM转到外部(保存为文件),就进行了编码转换,使用了具体的编码方案。因此可以说,所有的编码转换只发生在边界的地方,JVM和OS的交界处,也就是各种输入流/输出流(或者Reader,Writer类)起作用的地方。
所有的I/O基本上都分两大阵营:面向字符和面向字节。
面向字节,那么这类工作要保证系统中的文件二进制内容和读入JVM内存的二进制内容一致,不能变换任何0和1的顺序。这种输入/输出的方式很适合读入视频文件或者音频文件,或者任何不需要做变换的文件内容。
面向字符是希望系统中的文件字符和读入内存的字符一致。也就是说当我们从一个文本文件中读入字符的时候,这个字符如何编码暂且不看,我们需要的是,这个字符在读到内存里时,仍然是这个字符,不会乱。那从OS到JVM内存的过程中,其实是对字符进行了隐式的转换。
注意,上面提到过的Reader
和Writer
都是面向字符的I/O类,在输入输出的时候,都会使用自己默认的编码方式对文件进行转换。Reader和Writer的默认编码方式是GBK,这也就意味着,如果一个UTF-8文件使用Reader读入内存,它会使用GBK进行转换,那么打印出来的字符肯定不对。这是一种傻瓜式的功能提供方式,可能对大多数初级用户(不考虑跨平台)比较好用。
如果用到GBK编码以外的文件,就必须采用编码转换:一个字符与字节之间的转换。因此,Java的I/O系统中能够制定转换编码的地方,就是InputStreamReader
和OutputStreamWriter
类,他们是字节流和字符流的适配器,承担着编码转换的任务。
原文地址:http://blog.csdn.net/langduhualangdu/article/details/45420479