第一步,我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序),程序文件在保存时默认采用了操作系统默认支 持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件,也即,java程序在被编译前,我们的JAVA 源程序文件是采用操作系统默认支持的file.encoding编码格式保存的,java源程序中含有中文信息字符和英文程序代码;要查看系统的 file.encoding参数,可以用以下代码: public class ShowSystemDefaultEncoding { public static void main(String[] args) { String encoding = System.getProperty("file.encoding"); System.out.println(encoding); }} 第 二步,我们用JDK的javac.exe文件编译我们的Java源程序,由于JDK是国际版的,在编译的时候,如果我们没有用-encoding参数指定 我们的JAVA源程序的编码格式,则javac.exe首先获得我们操作系统默认采用的编码格式,也即在编译java程序时,若我们不指定源程序文件的编 码格式,JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式,如WIN2k,它的值为GBK),然后JDK就 把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后,javac把转换后的 unicode格式的文件进行编译成.class类文件,此时.class文件是UNICODE编码的,它暂放在内存中,紧接着,JDK将此以 UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说,我们最终获得的.class文件是内容 以UNICODE编码格式保存的类文件,它内部包含我们源程序中的中文字符串,只不过此时它己经由file.encoding格式转化为UNICODE格 式了。 这一步中,对于JSP源程序文件是不同的,对于JSP,这个过程是这样的:即WEB容器调用JSP编译器,JSP编译器先查看 JSP文件中是否设置有文件编码格式,如果JSP文件中没有设置JSP文件的编码格式,则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格 式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类,然后再把它编译成UNICODE格式的class 类,并保存在临时文件夹中。如:在中文win2k上,WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式,然后编译成临时保存的 Servlet类,以响应用户的请求。 第三步,运行第二步编译出来的类,分为三种情况: A、 直接在console上运行的类 B、 EJB类和不可以直接运行的支持类(如JavaBean类) C、 JSP代码和Servlet类 D、 JAVA程序和数据库之间 下面我们分这四种情况来看。 A、直接在console上运行的类 这 种情况,运行该类首先需要JVM支持,即操作系统中必须安装有JRE。运行过程是这样的:首先java启动JVM,此时JVM读出操作系统中保存的 class文件并把内容读入内存中,此时内存中为UNICODE格式的class类,然后JVM运行它,如果此时此类需要接收用户输入,则类会默认用 file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存(用户可以设置输入流的编码格式)。程序运行后,产生的字符 串(UNICODE编码的)再回交给JVM,最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系 统显示接口并输出到界面上。 对于这种直接在console上运行的类,它的转化过程可用图1更加明确的表示出来: (不好意思,图传不上来,只好让大家自己去想像图的样子了,我想看了上文是可以想来图来的。) 以上每一步的转化都需要正确的编码格式转化,才能最终不出现乱码现象。 B、EJB类和不可以直接运行的支持类(如JavaBean类) 由 于EJB类和不可以直接运行的支持类,它们一般不与用户直接交互输入和输出,它们常常与其它的类进行交互输入和输出,所以它们在第二步被编译后,就形成了 内容是UNICODE编码的类保存在操作系统中了,以后只要它与其它的类之间的交互在参数传递过程中没有丢失,则它就会正确的运行。 这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来: 图2 (不好意思,图传不上来,只好让大家自己去想像图的样子了,我想看了上文是可以想来图来的。) C、JSP代码和Servlet类 经过第二步后,JSP文件也被转化为Servlets类文件,只不过它不像标准的Servlets一校存在于classes目录中,它存在于WEB容器的临时目录中,故这一步中我们也把它做为Servlets来看。 对 于Servlets,客户端请求它时,WEB容器调用它的JVM来运行Servlet,首先,JVM把Servlet的class类从系统中读出并装入内 存中,内存中是以UNICODE编码的Servlet类的代码,然后JVM在内存中运行该Servlet类,如果Servlet在运行的过程中,需要接受 从客户端传来的字符如:表单输入的值和URL中传入的值,此时如果程序中没有设定接受参数时采用的编码格式,则WEB容器会默认采用ISO-8859-1 编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出,输出的字符串是UNICODE 格式的,紧接着,容器将Servlet运行产生的UNICODE格式的串(如html语法,用户输出的串等)直接发送到客户端浏览器上并输出给用户,如果 此时指定了发送时输出的编码格式,则按指定的编码格式输出到浏览器上,如果没有指定,则默认按ISO-8859-1编码发送到客户的浏览器上。这种JSP 代码和Servlet类,它的转化过程可用图3更加明确地表示出来: (不好意思,图传不上来,只好让大家自己去想像图的样子了,我想看了上文是可以想来图来的。) D、Java程序和数据库之间 对 于几乎所有数据库的JDBC驱动程序,默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的,所以,我们的程序在向数据 库内存储包含中文的数据时,JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式,然后传递到数据库中,在数据库保 存数据时,它默认即以ISO-8859-1保存,所以,这是为什么我们常常在数据库中读出的中文数据是乱码。 对于JAVA程序和数据库之间的数据传递,我们可以用图4清晰地表示出来 图4(不好意思,图传不上来,只好让大家自己去想像图的样子了,我想看了上文是可以想来图来的。) 3、分析常见的JAVA中文问题几个必须清楚的原则 首先,经过上面的详细分析,我们可以清晰地看到,任何JAVA程序的生命期中,其编码转换的关键过程是在于:最初编译成class文件的转码和最终向用户输出的转码过程。 其次,我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种: *ISO-8859-1,8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码 *Cp1252,美国英语编码,同ANSI标准编码 *UTF-8,同unicode编码 *GB2312,同gb2312-80,gb2312-1980等编码 *GBK , 同MS936,它是gb2312的扩充 及其它的编码,如韩文、日文、繁体中文等。同时,我们要注意这些编码间的兼容关体系如下: unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集,即GBK编码是在gb2312上扩展来的。同时,GBK编码包含了20902个汉字,编码范围为:0x8140-0xfefe,所有的字符可以一一对应到UNICODE2.0中来。 再 次,对于放在操作系统中的.java源程序文件,在编译时,我们可以指定它内容的编码格式,具体来说用-encoding来指定。注意:如果源程序中含有 中文字符,而你用-encoding指定为其它的编码字符,显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312,无论我们 在什么系统上编译含有中文字符的JAVA源程序都不会有问题,它都会正确地将中文转化为UNICODE存储在class文件中。 然后,我 们必须清楚,几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的,同时,几乎所有的浏览器在传递参数时都是默认以 UTF-8的方式来传递参数的。所以,虽然我们的Java源文件在出入口的地方指定了正确的编码方式,但其在容器内部运行时还是以ISO-8859-1来 处理的。 4、中文问题的分类及其建议最优解决办法 了解以上JAVA处理文件的原理之后,我们就可以提出了一套建议最优的解决汉字问题的办法。 我们的目标是:我们在中文系统中编辑的含有中文字符串或进行中文处理的JAVA源程序经编译后可以移值到任何其它的操作系统中正确运行,或拿到其它操作系统中编译后能正确运行,能正确地传递中文和英文参数,能正确地和数据库交流中英文字符串。 我们的具体思路是:在JAVA程序转码的入口和出口及JAVA程序同用户有输入输出转换的地方限制编码方法使之正确即可。 |