事出有因
在向HttpURLConnection的输出流写入内容时,因没有设置charset,导致接收方对数据的验签不一致。
URL url = new URL(requestUrl); //打开连接 HttpURLConnection urlConn = (HttpURLConnection)url.openConnection(); ...... ...... //获取输出流 out = new OutputStreamWriter(urlConn.getOutputStream()); //发送请求参数 out.write(concatKeyValue(params, false)); out.flush()
回过头来看,问题看似挺容易解决,只需在获取输出流时,指定字符编码集
out = new OutputStreamWriter(urlConn.getOutputStream(), "utf-8");
但实则费了一番工夫。期间心里产生了过以下的问题:
1、String.getBytes(String charset)与String(Byte[] bytes, String charset)的使用。
2、什么场合下会涉及的字符编码的转换呢?
3、在不显示指定charset时,java使用Charset.defaultCharset()来获取默认字符编码。Charset.defaultCharset()的值来自哪里?
为了在以后的开发中,快速解决字符集相关问题。在这里对字符集做了一次比较系统的学习。
字符集与字符编码
字符是各种文字与符号的总称,比如语言符号、图形符号、标点。
字符集是字符的集合,每个字符集包含的字符个数不同,常见的字符集有:unicode字符集、ISO 8859字符集、GB2312字符集、GB18030字符集、BIG5字符集等。
字符编码是以二进制数字来对应字符集中的字符,使得计算机能够识别字符集中的字符。
各个国家在制定编码标准的时候,通常是同时制定字符集与编码规范。因此常见的字符集,如GB2312,除了表示“字符集”这一层含义外,也包含了编码的含义。
Unicode字符集有多种编码方式,如UTF-8、UTF-16等;ASCII只有一种;大多数MBCS(Multi-Byte Chactacter System,即多字节字符系统)(包括GB2312)也只有一种。
Java
Java中的字节码文件始终以unicode编码方式保存。Java运行时创建的字符串变量、常量等字符串均以unicode编码存储在内存中。
当涉及数据读写时,就会涉及字符的编码转换问题,例如从磁盘读取文件时、向磁盘写入文件时、接收网络发送来的数据时等。
数据存储到物理介质或通过物理介质传输时,都是以bit流的形态存在。而要传输正确的bit流,就需要声明和设置恰当的字符编码。
getBytes()与getBytes(charset)
当不指定字符集时,getBytes内部会获取当前操作系统的默认字符集。
getBytes获得字符串的指定编码规范对应的字节数组,此过程称为编码。
//s的字符编码方式为unicode(utf-16), jvm的默认编码规则 String s = "我们"; //得到s的gbk编码, unicode(utf-16) --> gbk byte[] bytes = s.getBytes("gbk");
String的构造函数String(byte[] bytes, String charset)
//解码,gbk --> unicode(utf-16) String p = new String(bytes, "gbk");
Charset.defaultCharset()
此方式用来获取默认字符集,默认字符集在jvm启动时已经根据系统环境确认,不可以修改(或者说修改并不会生效)。当jvm的启动时,也可以通过参数-Dfile.encoding指定字符集,或通过环境变量修改默认字符集。
另外也请注意我们运行main方法与web项目的不同,它们启动了jvm的不同实例,所以默认字符集可能不同。所以如果在main方法中打印Charset.defaultCharset().name()的值 与实际运行web项目时输入的值不同,也不要觉得奇怪。
但凡涉及中文(web开发、I/O、数据库读写等),乱码问题会与我们不期而遇,所以理解字符集和字符编码就很有必要。
另外:
当我们使用IDE(idea/eclipse)时,我们可以设置开发环境所使用字符集。请注意:IDE中设置的字符集只能代表IDE在存取源代码文件时使用了何种字符集,而不是class文件使用了何种字符集。