一、唯一的编码

JVM中是不允许多种编码方式的字符并存的。例如一个GBK2312的字符串后面跟着一个UTF-8的字符串,那么连接后的最终结果应该是什么编码呢?显然不管选什么编码都会出错。因此便规定java中字符只以一种形式存在,那就是Unicode(不选任何特定的编码,直接使用它们在字符集中的编号,这是统一 的唯一方法)。

二、Unicode编码

详细内容:https://www.ibm.com/developerworks/cn/java/unicode-programming-language/index.html?lnk=hm

简单总结如下:

Unicode编码又被称为“统一码”、“万国码”、“单一码”。它为每中语言中的每个字符都设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

三种编码方式:UTF-8、UTF-16、UTF-32(Unicode Transformation Format)

UTF-8可以根据字符的编码大小来决定要使用的字节数量,比如英文字符只需要一个字节,但是中文字符就会有2-4个字节。(最常用)

UTF-16存储数据为双字节或者四字节。

UTF-16储存数据为固定为四字节。

三、字符的组成和转换

一个字符分为两部分:JVM内部和OS的文件系统。在JVM内部统一使用Unicode表示,当这个字符被从JVM内部转移到外部(即保存为文件系统中的一个文件的内容时),就进行了编码转换,使用了具体的编码方式。因此可以说,所有的编码转换只发生在边界的地方,JVM和OS的边界处,也就是各种输入输出流起作用的地方。如下图

Java的良好编码规则 java用哪种编码模式_JVM

四、I\O的划分——面向字符的输入输出流\面向字节的输入输出流

这里所谓的“面向”,是指这些类在处理输入\输出的时候,在哪个意义上保持一致。

1.面向字节:这类工作要保证系统中的文件二进制内容和读入JVM中的二进制内容一致,不能变换任何0和1的顺序。这种输入输出方式很适合读入视频或音频等不需要做内容变换的文件。

2.面向字符:这类工作是指希望系统中的文件二进制内容和读入JVM的二进制内容一致,不在乎你是否改变0和1的顺序。例如,在我们的windowsxp系统中有一个GBK格式的“林”字,当我们使用面向字符的I/O把它读入内存并保存在一个char型变量中时,我不希望I/O系统直接把“林”字的GBK编码给放到这个char型字符中(我希望它可以进行压缩什么的,也可以使用它自己独创的方法来保存)。实际上,我并不关心这个char型变量的具体的二进制内容到底是什么,我只希望这个字符读进来之后仍然是个“林”字。

总结如下图

Java的良好编码规则 java用哪种编码模式_Unicode_02

五、具体的I/O类

Reader/Writer类:面向字符。在保存数据时做了隐式转换,在输出时,将内存中的Unicode字符使用系统的默认的编码方式进行了编码,而在输入时,将内存中已经编码过的字符使用默认编码方案进行了还原。

注意:Reader/Writer类只能使用这个默认的编码方案来进行转换,而不能为它指定特定的编码,因此如果在中文版的windowsxp系统中存放了一个UTF-8编码的文件。当采用Reader/Writer类来进行读入的时候,它还是会用GBK来做转换,转换后的内容当然会出错。因此如果用到GBK以外的编码就必须采用编码转换:一个字符与字节之间的转化。

Java的I/O系统中能够制定转换编码的地方,也就在字符和直接转换的地方,那就是InputStreamReader和OutputStreamWriter。这两个类是字符流和字节流之间的适配器,承担编码转换的任务。