Java编程中文乱码问题

之前编程时也遇到过乱码问题,但是都是直接百度看看怎么解决,没有系统的学习过为什么会出现乱码。昨天一个同学也碰巧提到这个问题,大家也都不是很清楚,今天晚上就看了几篇博客学习了一下乱码问题。

首先简单看看为什么要进行编码:
计算机只认识二进制0和1,而我们人类只认识文字语言。因此,必须建立一种从文字到0和1的映射。
所以,文字转换成0、1称为编码,相反,0、1转换成文字称为解码。
例如,我们用记事本编辑了“Hello World”,然后将其保存到计算机中,这其中就有一个把文字转换成0、1的过程,即编码。我们把文字语言称为字符,而把二进制0、1称为字节流,在计算机中实际,无论是文本、图片、视频,存储的都是字符编码所对应的二进制代码,这些代码称为字节流。然后,我们打开记事本的话,系统就会将进行解码工作,将字节流转换成文字,这就是我们平时看到的东西。

那为什么会出现乱码呢?
出现乱码的原因就是文本字符编码过程与字节流解码过程使用了不同的编码方式。

下面先来看看几种常见的编码方式:
ASCLL:美国定义的一张码表。
ISO 8859-1:欧洲定义的一张码表,兼容ASCLL
gb2312:兼容ASCLL,识别数千种中文,2个字节表示一个字符。
gbk:gb2312的升级版,识别2万多种中文。
gb18030:gbk 的升级版,包含大部分中文。
Unicode:世界统一的编码方式,2个字节表示一个字符。
UTF-8:Unicode升级版,能用一个字节表示就用一个字节,要用两个字节就用两个字节表示,相对于Unicode,可以节省空间,汉字一般占用3个字节。

基础知识了解之后,就来看看Java怎么进行编码:
①JAVA编码方式:
大多数国际性的软件内部均采用unicode编码方式,在软件运行时,他获得本地操作系统的默认编码方式(如何查看操作系统的编码方式:https://blog.****.net/zp357252539/article/details/79084480/),然后再将软件内部unicode编码以操作系统的编码方式进行解码显示出来。Java中的JDK和JVM便是如此。由于Java语言内部采用unicode编码,所以在Java程序运行时,就存在着一个从unicode编码和对应的操作系统及浏览器支持的编码格式输入、输出的问题,这个转换过程有一系列的步骤,如果其中任何一步出错,则显示出来的汉字就会是乱码。下面就来看看Java编码转换的具体步骤
②JAVA编码转换过程:
第一步,我们用操作系统的记事本编写一个java源程序文件,程序文件在保存时默认就采用了操作系统默认支持的编码格式进行保存,就形成了.java文件。

第二步,我们用JDK来编译Java源程序,由于JDK是国际版的,在编译时,如果我们没有用-encoding指定我们的Java源程序的编码方式,那么javac首先获得我们操作系统默认的编码方式来对java源程序进行读取编译,JDK就把我们的java源程序从file.encoding编码格式转换成Java内部的unicode编码格式,暂存内存中,javac把转换后的unicode文件进行编译为Unicode编码形式的class文件。因此,我们平时编译获得class文件是Unicode编码格式的。
因此,Java源程序进行编译的编码方式一定要和Java保存源文件的编码方式一样才行,不然会出错。
这一步中,对于JSP源程序文件是不同的,JSP过程是:WEB容器调用JSP编译器,JSP编译器先查看JSP文件是否设置有文件编码方式(一般通过pageEncoding属性或charset属性来设置),如果没有,JSP编译器就使用操作系统的编码方式将其转化为临时的Servlet类,然后在将其编译成unicode编码的class文件暂存内存之中,在将unicode编码格式的class文件编码成Unicode形式存储在计算机中。

第三步,运行编译时的类,这里会存在以下几种情况:
1.直接在控制台运行
2.JSP/Servlet类
3.Java类与数据库之间

下面先来看第一种
①直接在控制台运行:
现在我们一般开发都是在IDE中运行,例如我用的Intellij Idea,他都给你设置了默认编码为UTF-8。这种情况下,JVM首先会把保存在操作系统中的class文件读取到内存之中,这个时候内存中class文件是以Unicode编码格式保存在内存中的,然后JVM运行它。如果需要用户输入信息,则会采用file.encoding编码格式(在我的Intellij Idea里,使用默认的UTF-8编码)对用户输入的信息进行编码同时转化为Unicode编码格式保存到内存之中,程序处理完结果之后,将产生的结果再转化为file.encoding格式返回到操作系统的输入界面中去。这种情况出现乱码的可能性很低,只要你不中间随意改动编码方式,一般不会出现乱码,整个流程如下:
Java编程中文乱码问题

②JSP/Servlet:
这种情况下,出现乱码的可能性就大多了。
先看看其过程:当用户请求一个Servlet时,首先JVM会把Servlet的class加载到内存中去,内存中的class文件是以Unicode形式编码,然后JVM运行该Servlet类,在运行过程中如果需要接受从客户端传递过来的数据(如表单和URL传递的数据),则WEB容器会接受传入的数据,在接收过程中如果程序设定了传入参数的的编码(使用request.setCharacterEncoding()来设置服务器接收数据时的编码转换方式,注意此编码方式要和charset中设置的编码方式一致,等下会谈到这个问题)则采用设定的编码格式,如果没有设置则采用默认的ISO-8859-1编码格式,接收的数据后JVM会将这些数据进行编码格式转换为Unicode并且存入到内存中。运行Servlet后产生输出结果,同时这些输出结果的编码格式仍然为Unicode。紧接着WEB容器会将产生的Unicode编码格式的字符串直接发送置客户端,如果程序指定了输出时的编码格式,则按照指定的编码格式输出到浏览器,否则采用默认的ISO-8859-1编码格式。整个过程流程图如下:
Java编程中文乱码问题
JSP中有多处地方需要进行编码设置问题:

pageEncoding和charset的区别:Java编程中文乱码问题
Java编程中文乱码问题
pageEncoding设置的是JSP文件本身的编码方式,即源程序保存和编译时的编码方式(这两种编码方式一定要一样,否则编译不会成功)。如果pageEncoding属性不存在,则由contentType属性中的charset决定,如果两者均无,则使用默认的ISO-8859-1编码方式进行保存源程序和编译。
contentType的charset设置的是服务器发送给客户端的内容编码方式。
JSP要经过两次的“编码”:第一阶段用pageEncoding进行编译成Unicode的Servlet和classes文件。第二阶段由Tomcat出来的网页,用的是charset编码方式。pageEncoding和charset可以设置为不同的字符集,但是不能设置为不支持中文的编码方式。

request.setCharacterEncoding(“ ”)设置的是服务器接受数据以什么编码方式输出,即从request中取的值或从数据库中取出的值,指定之后可以通过getParameter()获得正确的字符串,这个指定的编码需要和前面设置的charset设置的编码一致。如果不设置,则默认使用ISO-5589-1编码。
response.setContentType(“text/html;charset=UTF-8”);设置浏览器以UTF-8编码进行接收,一般在JSP中已经设置好了,但是Servlet中没有设置。

③我们知道java程序与数据库的连接都是通过JDBC驱动程序来连接的,而JDBC驱动程序默认的是ISO-8859-1编码格式的,也就是说我们通过java程序向数据库传递数据时,JDBC首先会将Unicode编码格式转换为ISO-8859-1的编码格式,然后存储在数据库中,即数据库在保存数据时,默认格式为ISO-8859-1.
Java编程中文乱码问题
可以将自己的数据库编码设置为UTF-8

基础知识了解之后,才能对乱码问题有个了解。下面还有几个原则需要提出:
①几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的,同时,几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以,虽然我们的Java源文件在出入口的地方指定了正确的编码方式,但其在容器内部运行时还是以ISO-8859-1来处理的。

②通过Get方法向后台提交请求:
向服务器递交的请求中,字符也是需要编码之后传输的。浏览器将自动对URL地址中的汉字进行编码之后传输。如果你直接在URL地址中输入汉字,浏览器会根据当前操作系统
的默认字符集进行编码(可以设置Tomcat的server.xml文件设置URIEncoding)再传输到服务器;如果是在某个页面中点击的附带中文的连接,那么浏览器会根据这个页面所使用的字符集对汉字进行编码之后,传输到服务器。

③通过Post方法向后台递交请求:
浏览器将按照表单所在页面所使用的字符集对数据进行编码之后传输。在Tomcat中配置URIEncoding只对Get请求有效;对于Post请求,则必须在调用request.getParameter(“XXX”)之前,调用request.setCharacterEncoding(“UTF-8”);

总结:
在JAVA程序转码的入口和出口及JAVA程序同用户有输入输出转换的地方限制编码方法使之正确即可有效避免中文乱码问题。
参考:
https://www.cnblogs.com/chenssy/p/4207554.html
https://www.cnblogs.com/liushuncheng/p/6891307.html?utm_source=itdadao&utm_medium=referral
如有错误,恳请给予指出,万分感谢。