黑马程序员技术交流社区
标题: Java字符集编码问题 [打印本页]
作者: 我能学编程吗 时间: 2013-11-3 01:37
标题: Java字符集编码问题
本帖最后由 我能学编程吗 于 2013-11-3 01:44 编辑
看张孝详老师的基础视频,有讲到字符编码的知识,看着他的代码一模一样的敲都得不到相同结果,我简化后如下:- //iso8859-1:西方国家使用的字符集
- System.setProperty("file.encoding","iso8859-1");//修改JAVA系统使用的默认字符编码为iso8859-1
- System.getProperties().list(System.out);//打印JAVA系统属性,从中查看file.encoding字符集
- String strChina="中国";
- byte [] buf=strChina.getBytes();//按默认字符集(即iso8859-1)对strChina编码,并转换成byte[]
- for(int k=0;k<buf.length;k++) {
- System.out.write(buf[k]);//这里传的必须是gbk编码的字节才能正确显示中文
- }
- System.out.println(); // 这里会自动调用flush,这样上面写的内容才会输出。说明System.out也是用了缓冲技术的。
复制代码 要求: 1、修改Java默认的字符集编码:System.setProperty("file.encoding","iso8859-1");
2、修改后String类的.getBytes();方法没指定字符集的话,就会使用默认的字符集编码,即上面设置的iso8859-1,所以上面代码中得到的buf编码应该是iso8859-1类型的
3、System.out.write(buf[k])写到显示器上,这里写的是iso8859-1编码字节的数据,我们知道电脑的默认是使用gbk进行显示的,应该传gbk编码的数据才能正确显示。
4、为什么电脑仍然输出的是正常的“中国”两字,说明我调用getBytes();方法的时候拿到的依然是gbk编码的字节数据,这是为什么啊?
麻烦大家跑一下这代码,看是输出“中国”,还是乱码。我看张孝详老师的可是会输出乱码的。
作者: 魏春旭 时间: 2013-11-3 08:31
{:soso_e127:}我还没看张老师的视频。
作者: 黄炳期 时间: 2013-11-3 10:52
抓紧时间哦!
作者: --_.Is’攸稀 时间: 2013-11-3 12:30
没看张孝详老师的视频,也没学到那么深,所以就只能帮你运行一下了。
-
1.PNG
(56.28 KB, 下载次数: 68)
作者: ∏艾力克斯※ 时间: 2013-11-3 12:34
- // iso8859-1:西方国家使用的字符集
- System.setProperty("file.encoding", "");// 修改JAVA系统使用的默认字符编码为iso8859-1
- System.getProperties().list(System.out);// 打印JAVA系统属性,从中查看file.encoding字符集
- String strChina = "中国";
- byte[] buf = strChina.getBytes();// 按默认字符集(即iso8859-1)对strChina编码,并转换成byte[]
- for (int k = 0; k < buf.length; k++) {
- System.out.write(buf[k]);// 这里传的必须是gbk编码的字节才能正确显示中文
- }
- System.out.println(); // 这里会自动调用flush,这样上面写的内容才会输出。说明System.out也是用了缓冲技术的。
复制代码 修改后,以不输入字符编码,或者干脆输入一个不存在的字符编码,均会输出结果“中国”,不乱码。
可见,System.setProperty("file.encoding", "123");这行代码已经失去作用,无效化。
本人推测,是运行环境所导致的问题,项目编码之类的可能会影响,但是没找到具体原因
作者: 黄炳期 时间: 2013-11-3 13:28
如果问题已经解决,请及时修改主题为“提问结束”。
修改主题的方法链接
http://bbs.itheima.com/thread-89313-1-1.html
作者: Mr.Z.Lee 时间: 2013-11-3 15:16
是正常的不是乱码
作者: 我能学编程吗 时间: 2013-11-3 16:31
还木有解决呢,具体原因不知道啊!
作者: 我能学编程吗 时间: 2013-11-3 19:27
本帖最后由 我能学编程吗 于 2013-11-3 19:36 编辑
终于,通过自己看源代码,我知道原因了:
查源代码,肯定是查返回byte字节的地方,点击getBytes()查看源码如下:
public byte[] getBytes() {
return StringCoding.encode(value, offset, count);
}
这里看到调用了ecode方法,并且该方法没有任何的注释,所以只能再进源代码看了,点击进入encode方法的源代码如下:
static byte[] encode(char[] ca, int off, int len) {
String csn = Charset.defaultCharset().name();
.....多句代码
}
此处第一句很明显是获取到了默认的字符编码的名称,查看JDK文档也有这个方法说明。把这句代码复制出来运行,可看到结果是GBK。为什么是GBK呢?只有再进源码再看了,点击defaultCharset方法进入源码,如下:
public static Charset defaultCharset() {
if (defaultCharset == null) {
synchronized (Charset.class) {
java.security.PrivilegedAction pa = new GetPropertyAction("file.encoding");
String csn = (String)AccessController.doPrivileged(pa);
Charset cs = lookup(csn);
if (cs != null) defaultCharset = cs;
else defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
从上面源码可知,如果成员defaultCharset不为空,则直接返回,否则就是去获取一个字符集了,在代码中看到有new GetPropertyAction("file.encoding"),猜想它应该是要去获取Java环境属性中的“file.encoding”属性,
而代码:String csn = (String)AccessController.doPrivileged(pa); 一猜就是获取“file.encoding”属性的值,变量名应该是CharSetName的缩写吧。
而代码:Charset cs = lookup(csn); 很明显这句就是返名字为csn对应的字符集对象。Lookup方法没有注释说明。
而forName("UTF-8");是返回编码为"UTF-8"的字符集,这个方法有注释,也可在JDK文档中查看说明,再看这个方法的源码如下:
public static Charset forName(String charsetName) {
Charset cs = lookup(charsetName);
if (cs != null)
return cs;
throw new UnsupportedCharsetException(charsetName);
}
从上面源码可知,forName方法最终也是调用lookup方法返回一个字符集对象的。
综合上面的理解,可把上面的public static Charset defaultCharset()方法简化如下:
public static Charset defaultCharset() {
if (defaultCharset == null) {
Charset cs = 获取Java环境变量"file.encoding"中指定的字符集
if (cs != null) defaultCharset = cs;
else defaultCharset = forName("UTF-8");
}
}
return defaultCharset;
}
因此我们调用String csn = Charset.defaultCharset().name();时得到csn的值为GBK,说明上面的代码没有去获取"file.encoding"中指定的字符集,也就是说明上面代码中defaultCharset的值默认就有值了,而且是“GBK”,所以知道了:就算我改变了file.encoding变量的值,但是系统也不会查找这个变量的值来当默认的编码。因此,可以说张孝详老师的代码没有错,我们的代码也没有错,只是老师录那个视频时用的JDK版本比较低,而现在高版本的代码对这部份进行了修改。
空想没凭证,百度学习了一下反射,验证过程如下:
上面的源码中defaultCharset变量的声明的源码为:private static volatile Charset defaultCharset; ,一看是静态的,而且私有的,这样就不能访问这个变量了,所以采用了反射技术拿到这个变量,并将它的值修改为 null ,当defaultCharset == null 时,则上面public static Charset defaultCharset()方法就会执行 “获取Java环境变量"file.encoding"中指定的字符集” 的那些代码,运行结果证明,我是正确的,代码如下:
//修改JAVA系统使用的默认字符编码为iso8859-1
System.setProperty("file.encoding","iso8859-1");
// 通过反射技术,把Charset.defaultCharset的值修改为null
Class clazz = Class.forName("java.nio.charset.Charset");// 获取Charset的字节码对象
Field field = clazz.getDeclaredField("defaultCharset"); // 获取字段(包括私有的),这里获取的是私有的成员属性:defaultCharset
field.setAccessible(true); // 设置私有字段可以被访问
field.set(clazz, null); // 修改defaultCharset成员的值为null
// 如果Charset.defaultCharset == null,即会去获取环境变量file.encoding中指定的字符集
String csn = Charset.defaultCharset().name(); // 获取默认的字符集的名字
System.out.println(csn);
String strChina = "中国";
byte[] buf = strChina.getBytes(); // 此处拿到的字节数据将是"iso8859-1"编码类型的
System.out.write(buf); // 只有传GBK类型的才能正确显示中文字符,所以此处是乱码
System.out.flush();
通过使用反射知识后,感觉自己的技术一下子就提升了,好牛 B 啊!
所以仔细一想,老师的方法已经过时,上面的代码中,不应该使用设置Java的环境变量file.encoding的值,来改变Java默认使用的编码,file.encoding默认是GBK,不要改变它,应该直接改变Charset.defaultCharset变量的方式来改变默认编码,在上面的代码中把修改file.encoding的代码注释,并增加如下代码:
Charset iso8859 = Charset.forName("ISO8859-1"); // 创建一个"ISO8859-1"的字符集
field.set(clazz, iso8859); // 修改defaultCharset成员的值为"ISO8859-1"字符集
哇!好牛叉,我自己回答自己的问题,而且回答的这么详细,版主是否应该给加1分呢?
补充:Charset.defaultCharset变量的值是怎么默认就为GBK的呢?这个我就没有再去深究了,已经究不下去了,但是已经足够了,通过看源码已经让我学到许多东西了!
不过我可以猜想一下:Charset这个类是要比我们写的类先装进内存的,JVM自动调用了Charset.defaultCharset()方法,此方法会去查询Java环境变量file.encoding的值,此时的值是默认的GBK,所以JVM是首先把Charset.defaultCharset设置了GBK。JVM自己的一些必须的类都启动完成了,才是启动我们写的类,虽然我们改变了file.encoding的值,但是Charset.defaultCharset的值已经初始化好了,已经是GBK了。
作者: ⋛⋌⋚JEEP 时间: 2014-7-15 21:53
lz牛逼,学习了:lol
作者: a191170796 时间: 2014-7-16 10:41
楼上的!跟着你有汤喝啊!
作者: nxp1986 时间: 2014-7-17 22:54
lz牛逼,我也跟着学习了
作者: 刘斌2014 时间: 2014-7-27 12:43
看了一下别人的解答 我也懂了
作者: 黑马-胡明 时间: 2014-7-27 12:59
厉害吧···
作者: 日光加蓝 时间: 2014-7-28 12:25
。。。。。。。。。。。。。。。。。。。。。。。。。
作者: 狐狸FMF 时间: 2014-8-13 10:37
java 真是 学无止境啊
作者: 狐狸FMF 时间: 2014-8-14 23:32
楼主真给力
作者: 黄小橙 时间: 2014-10-4 09:25
学习了!谢谢
作者: wujiemin 时间: 2014-10-7 09:18
求张泽华老师讲课时用的color.exe和size.exe工具
作者: wujiemin 时间: 2014-10-8 09:21
为了实现梦想;让自己活着更有意义 做自己有兴趣的事
作者: hamesksk 时间: 2015-3-16 00:54
是正常的不是乱码
作者: 吴阳 时间: 2015-3-16 14:00
提前学习了
作者: 等风的车 时间: 2015-3-16 14:01
我的也是乱码
作者: yanfeng 时间: 2015-7-19 20:39
张孝祥老师的视频不错啊
作者: yuanyuemao88 时间: 2015-8-2 17:38
还在看基础视频,苦逼啦
作者: cloud1991 时间: 2015-9-15 15:05
表示不太懂,学习一下
作者: csc 时间: 2015-12-25 01:37
多谢楼主
作者: 存在着的天空 时间: 2015-12-25 21:06
JAVA无论是什么时候都有很大的用处
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |