一直在试图搞清楚java中的编码问题,也看了网上的一些文章,但仍是云里雾里。直到最近看了方立勋老师的web课程,才略略明白一点。html
在此记录一下本身的理解,看看本身能不能说清楚。java
第一个问题:我在java代码中定义了一个字符串,它是什么编码?node
字符串实质是一个char数组。那么char的编码,其实就是字符串的编码。那么char什么编码呢?为何'中'字转int类型后的值是20013呢?web
char c = '中'; System.out.println(c); // 中 System.out.println((int) c); // 20013
引入第一个概念:Unicode编码数组
Unicode是一种“编码”,所谓编码就是一个编号(数字)到字符的一种映射关系,就仅仅是一种一对一的映射而已。浏览器
java的String使用的编码是Unicode。tomcat
来个简单的例子证实:服务器
找个转码工具,将'中'字转为Unicode编码,结果是'\u4e2d'。用'\u4e2d'替换原来的'中'字。打印的结果和'中'字是同样的。网络
char c = '\u4e2d'; System.out.println(c); // 中 System.out.println((int) c); // 20013
'\u'是什么意思?jsp
'\u'的意思就是使用了Unicode编码。后面加上十六进制代码来表示Unicode字符。下面这段代码能够验证。
Integer num = Integer.valueOf("4e2d", 16); System.out.println(num.intValue()); // 20013
第二个问题:编码和编码格式的区别是什么?
这部份内容来自文章 java中的编码和编码格式问题 做者:风未馨
1. Unicode是一种“编码”,所谓编码就是一个编号(数字)到字符的一种映射关系,就仅仅是一种一对一的映射而已。
2. GBK、UTF-8是一种“编码格式”,是用来序列化或存储1中提到的那个“编号(数字)”的一种“格式”。
编码和编码格式:
java的String使用的编码是Unicode,当String存在于内存中时(在代码中用string类型的引用对它进行操做时),是"只有编码而没有编码格式的",因此java程序中的任何String对象,说它是gbk仍是utf-8都是错的,String在内存中不须要“编码格式”, 它只是一个Unicode的字符串而已。
当字符串须要在网络中传输或要被写入文件时,就须要使用编码格式了。乱码问题也所以出现。
GBK和UTF-8:
GBK和UTF-8都是用来序列化或存储Unicode编码的数据的,可是分别是2种不一样的格式,他们都是Unicode编码的实现方式;他们俩除了格式不同以外,他们所关心的Unicode编码范围也不同。
UTF-8考虑了不少种不一样国家的字符,涵盖整个unicode码表,因此其存储一个字符的编码的时候,使用的字节长度也从1字节到4字节不等;
而GBK只考虑中文——在Unicode中的一小部分的字符的编码,因此它算好了只要2个字节就能涵盖到绝大多数经常使用中文(2个字节能表示6w多种字符),因此它存储一个字符的时候,所用的字节长度是固定的;
ASCII码和Unicode:
ASCII码,和Unicode编码同样,也是一种"编码"。
ASCII码的范围比较小,一共规定了128个字符的编码。
Unicode编码是一个很大的集合,如今的规模能够容纳100多万个符号。就像它的名字都表示的,这是一种包含全部符号的编码。
固然也有其余的编码,没用过,我也不甚了解。
第三个问题:哪些地方会用到编码格式?
前文中提到过一句话:当字符串须要在网络中传输或要被写入文件时,就须要使用编码格式了。
网络中传输:
对于java web开发人员,指的就是java web了。那在java web中,哪些地方须要设置编码格式呢?
先上个图(网上随便拷贝来的)。假定浏览器就是ie,WEB服务器是tomcat,页面是jsp,应用服务器用的是servlet。
1. 浏览器中打开了一个页面,这个页面映射应用服务的某个jsp。此时浏览器中的解码格式是什么呢?
浏览器的解码格式是在jsp中指定的,好比在jsp文件中常常能够看到这样两行代码。
<!--
这一句是和Tomcat说的:保存在硬盘上的jsp文件在被Tomcat翻译成servlet的时候,使用utf-8解码jsp文件的内容。
若是不指定,会默认使用iso-8859-1来解码。
-->
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!-- 这一行是和浏览器说的:浏览器解码的时候请使用utf-8解码哦 -->
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
这样浏览在解析这个jsp的时候,就是使用utf-8编码来解码。
浏览器的解码格式也是能够在浏览器上设置的:
若是网页中出现了乱码,你换一种编码格式来解码,可能就不乱了。
2. response中也能够设置内容的编码格式和指定浏览器的解码格式。
开发人员应该都知道,jsp就是一个特殊一点的servlet。在servlet中, response有两个设置编码的方法,和jsp中的那两行编码配置有着类似的功能。
它们就是:
// 表示response的内容会以utf-8的编码方式编码后发送给浏览器。
response.setCharacterEncoding("UTF-8");
// 告诉浏览器,解码的时候也要使用utf-8解码哦。 response.setContentType("text/html;charset=UTF-8");
3. 经过request对象能够指定应用服务用哪一种编码格式来解码接收到的数据.
上面两种状况,都是讲servlet本身如何编码,而后告诉浏览器如何解码的。servlet也能够指定对接收到的对象使用哪一种编码格式来解码。
// 经过这句话,能够指定用utf-8编码格式来解码浏览器传来的数据。不过只对post方式传来的数据有效。若是是get方法传来的数据,仍是会以默认的iso-8859-1来解码。
request.setCharacterEncoding("UTF-8");
4. 设置tomcat服务器配置文件server.xml,指定以何种编码解码浏览器传来的参数。
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" URIEncoding="UTF-8" />
'URIEncoding="UTF-8"', 这个属性的配置, 和"request.setCharacterEncoding("UTF-8");"这句代码的功能大体相同。都是指定tomcat服务器接受到收据后如何解码。
若是不指定,tomcat服务器会默认使用iso-8859-1来解码。
此处建议不要使用URIEncoding指定编码,由于一个tomcat服务器里可能会有多个应用,由于某一个应用修改tomcat配置可能会致使其余应用出错,编码转换在每一个应用中单独处理便可。
被写入文件:
1. 文件内容在被写入文件时是被编码了的,默认的编码格式是gb2312(可能和系统有关,简体中文系统测试,就是gb2312)。
能够在代码中指定文件流在写入文件时用什么编码格式。好比指定用"utf-8".
Writer write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
2. 文件自己不一样软件是有选择性的支持能够对哪些编码进行解码。
好比:
Excel支持gb2312,不支持UTF-8。
Txt记事本支持UTF-8编码。
因此有时候,我下载一个csv文件(指定内容使用utf-8编码)。会遇到这样的状况:
这个csv文件在使用excel打开的时候,中文是乱码的,若是换成txt打开,中文就正常显示了。
这个时候若是想要在excel中不乱码,就须要在代码中指定内容使用gb2312编码。
也能够经过txt的'另存为',改变文本的编码格式。
3. 如何肯定文件的编码呢?
我在win7(简体中文)系统中,右键->新建一个文本文档,nodepad++打开后,查看编码,能够看到编码是ANSI。
使用java代码生成一个txt文件,未明确指定编码格式,nodepad++打开后,查看编码,能够看到编码是utf-8。
Eclipse中作了一个小测试:
test1. 指定utf-8编码格式
String fullPath = "D:\\test2.txt"; File file = new File(fullPath); if (!file.exists()) { file.createNewFile(); } Writer write = new OutputStreamWriter(new FileOutputStream(file), "utf-8"); write.write("你好"); write.flush(); write.close();
生成的文件编码格式是:
test2. 指定gb2312编码格式
String fullPath = "D:\\test2.txt"; File file = new File(fullPath); if (!file.exists()) { file.createNewFile(); } Writer write = new OutputStreamWriter(new FileOutputStream(file), "gb2312"); write.write("你好"); write.flush(); write.close();
生成的文件编码格式是:
test3. 先使用utf-8生成文件,输出"你好",再使用gb2312,追加内容,输出"你好",结果是这样子的。
总结:
第一次使用utf-8编码格式输出你好,此时文件内容 第一个"你好" 是utf-8编码的,文件的解码方式也是utf-8.
第二次使用gb2312编码格式输出你好,此时的文件追加的内容 第二个"你好" 是gb2312编码的,文件的解码方式也变成了gb2312。
也就是说,Eclipse操做文本文件TXT时,txt文件会一直使用最后一次操做文件使用的编码格式来解码文件的内容。
因此第二次输出"你好"后,第一次输出的"你好"变成了乱码。