面试之Java String 编码相关
实话说,作为一个多年Java老年程序员,直到近来,在没有决心花时间搞清楚Java String的编码相关问题之前, 自己也都还是似懂非懂,一脸懵逼的。设想如果在面试中,有同学能够条理清晰的回答下面的问题,那必是非常了得之人,论智慧武功应该均在本人之上:-)。
问:请预测下面程序的输出,并解释原因。printHexBinary方法为16进制打印Byte
1 String str = "中";
2
3 byte[] bufferGBK = str.getBytes("GBK");
4 System.out.println("bufferGBK = "+printHexBinary(bufferGBK)) ;
5
6 String gbkString =new String(bufferGBK,"GBK");
7 System.out.println("gbkString = new String bufferGBK GBK : "+gbkString);
8
9 String utf8String =new String(bufferGBK,"utf-8");
10 System.out.println("utf8String = new String bufferGBK utf8 : "+utf8String);
11
12 byte[] utfFromStr = utf8String.getBytes("utf-8");
13 System.out.println("utf8String getBytes utf-8 : "+printHexBinary(utfFromStr));
14
15 byte[] gbkFromStr = utf8String.getBytes("GBK");
16 System.out.println("utf8String getBytes GBK : "+printHexBinary(gbkFromStr));
17
18 byte[] isoFromStr = utf8String.getBytes("ISO-8859-1");
19 System.out.println("utf8String getBytes ISO-8859-1 : "+printHexBinary(isoFromStr));
20
21 String isoString =new String(bufferGBK,"ISO-8859-1");
22 System.out.println("isoString = new String bufferGBK ISO-8859-1 : "+isoString);
23
24 utfFromStr = isoString.getBytes("utf-8");
25 System.out.println("isoString getBytes utf-8 : "+printHexBinary(utfFromStr));
26
27 gbkFromStr = isoString.getBytes("GBK");
28 System.out.println("isoString getBytes GBK : "+printHexBinary(gbkFromStr));
29
30 isoFromStr = isoString.getBytes("ISO-8859-1");
31 System.out.println("isoString getBytes ISO-8859-1 : "+printHexBinary(isoFromStr));
按我之前的认识,先简单推理下。
第4行的Print输出的应该是“中”的GBK编码(中的GBK编码是0xD6 0xD0)。
第7行用[0xD6 0xD0]以GBK字符集new一个String,打印这个String,那应该是“中”
第10行用[0xD6 0xD0]以UTF8字符集new一个String,打印这个String,这里可能会乱码,具体会显示什么字符,要看0xD6 0xD0对应的Utf8 字符。
× 第13行从上面new的String中按UTF8取得Byte数组,因为上面New 的是Utf8 String,这里取出的应该还是[0xD6 0xD0]
× 第16行从上面new的String中按GBK取得Byte数组, 这……不太确定,可能还是[0xD6 0xD0]?内存存储的编码应该是不变的?
× 第19行从上面new的String中按ISO8859取得Byte数组, 这……同上吧? 但似乎有点儿问题,应该是不对,逻辑上如果getBytes都一样,那为啥要参数指定字符集呢?
第22行用[0xD6 0xD0]以ISO8859字符集new一个String,打印这个String,这里可能会乱码, 要看[0xD6 0xD0]ISO8859中对应的字符。
× 第25,28行,这……
第30行从上面new的String中按ISO8859取得Byte数组,这应该不会变,还是[0xD6 0xD0]
我只能回答成这样了,自我感觉比较风流倜傥,潇洒惆怅的可以先自己琢磨下, 实际的程序输出在这里↓

1 ========================================
2 bufferGBK = 0xD6,0xD0
3 gbkString = new String bufferGBK GBK : 中
4 utf8String = new String bufferGBK utf8 : ��
5 utf8String getBytes utf-8 : 0xEF,0xBF,0xBD,0xEF,0xBF,0xBD
6 utf8String getBytes GBK : 0x3F,0x3F
7 utf8String getBytes ISO-8859-1 : 0x3F,0x3F
8 isoString = new String bufferGBK ISO-8859-1 : ÖÐ
9 isoString getBytes utf-8 : 0xC3,0x96,0xC3,0x90
10 isoString getBytes GBK : 0x3F,0x3F
11 isoString getBytes ISO-8859-1 : 0xD6,0xD0
12 ========================================
答案点这里
然后对着输出结果来理解下。
答案中的2,3行输出跟预期一样
第4行确实是“乱码”了,但为什么[0xD6 0xD0]会变成两个一样的字符��
第5行,byte数组不是之前的2个,而是6个元素,与0xD6 0xD0完全不同,是何原因?
第6,7行,byte数组是[0x3F 0x3F],为啥?
第8行,也是“乱码”了,ÖÐ, 但为什么又变成了两个不同的字符。。-_-||
第9行 byte数组4个元素,看起来不同。
第10行 byte数组[0x3f 0x3f]
第11行 确实还是[0xD6 0xD0]
实践检验真理,上面的实验表明,String在内存存储的实际内容与getBytes取得的内容,可能是存在转换关系的。某些字符集的情况下是不变的(ISO8859),而有些经过Byte 到 String 到 Byte 的转换后会发生变化,与创建时的byte数组不同。
经过一番上下求索之后。下面是我认为比较合理的解释。
答案中的2,3行输出跟预期一样
第4行,乱码因为[0xD6 0xD0]不是两个有效的Utf8字符集字符, Java将其转换处理为两个�,即utf8String中的内容即为“��”
第5行此时取得Byte数组为对应Utf8 中两个�字符的字符编码,即在UTF8 字符集中� 的编码为[0xEF,0xBF,0xBD]
第6行取得的Byte数组为,字符�对应在GBK字符集中的字符编码,该字符应该未包含,被转换为 0x3F 即 ? 字符
第7行,同上
第8行,并不是乱码,Ö 和 Ð 确实是ISO8859字符集中包含的字符,对应的编码为[0xD6 0xD0],在GBK中为字符 “中” ,在 ISO8859中为两个字符 “Ö” 和 “Д,isoString内容为“ÖД
第9行,取得isoString在utf8 编码集中对应 Ö 和 Ð 字符的编码数组, 即 [0xC3,0x96] =Ö [0xC3,0x90] = Ð。
第10行,取得isoString在GBK编码其中对应的Ö 和 Ð 字符的编码数组,因为GBK未包含这两个字符,于是被转换为“??”后取得编码 即 [0x3F 0x3F]
第10行,取得isoString在ISO8859中对应的Ö 和 Ð 字符的编码数组,即为[0xD6 0xD0],因此不变。
总结及推论:
- String实际存储的内容是不可见,也无需关心的,可以理解为它存储的是字符。你用Byte数组初始化一个字符串时,总会显示或者默认的指明数组的编码格式。String内部会据此将其对应的字符而非编码,以某种方法保存在其内部。如果你指定的字符集与提供的数组不一致,String会帮你映射为未知字符可能是“?”或“�”。
- String存储的不是初始化时提供的Byte数组,因此经过 Byte 到 String的转换后,可能会导致原始Byte数组的内容丢失,无法通过转换后的 String获得。所以乱码问题,要从源头解决,而不是在String上下功夫。
- ISO8859-1是一个0x00-0xFF的都有定义的单字符编码,因此该编码进行byte到String转换不会丢失信息,String可以以Iso8859取得Byte数组后,以其他字符集显示,因此很多地方仍然使用此种字符集。
另:字符是抽象的,具体存储肯定要定义编码,Java规范定义的是“外部”的编码的表现和工作方式,内部存储可以自行实现,目前实际使用似乎是UTF16.
面试之Java String 编码相关的更多相关文章
- Java String的相关性质分析
引言 String可以说是在Java开发中必不可缺的一种类,String容易忽略的细节也很多,对String的了解程度也反映了一个Java程序员的基本功.下面就由一个面试题来引出对String的剖析. ...
- java String编码转换
/** * Get XML String of utf-8 * * @return XML-Formed string */ public static String getUTF8XMLString ...
- Java String类相关知识梳理(含字符串常量池(String Pool)知识)
目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...
- 面试话痨(二)C:JAVA String,别以为你穿个马甲我就不认识你了
面试话痨系列是从技术广度的角度去回答面试官提的问题,适合萌新观看! 面试官,别再问我火箭怎么造了,我知道螺丝的四种拧法,你想听吗? String相关的题目,是面试中经常考察的点,当面试中遇到了St ...
- 从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念
转(http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html#0-tsina-1-10971-397232819ff9a ...
- 手写代码 - java.lang.String/StringBuilder 相关
语言:Java 9-截取某个区间的string /** * Returns a string that is a substring of this string. The * substring b ...
- java 中String编码和byte 解码总结——字节流和字符流
1.InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符 InputStreamReader(InputStream in, Strin ...
- Java总结篇系列:Java String
String作为Java中最常用的引用类型,相对来说基本上都比较熟悉,无论在平时的编码过程中还是在笔试面试中,String都很受到青睐,然而,在使用String过程中,又有较多需要注意的细节之处. 1 ...
- 通过反编译深入理解Java String及intern(转)
通过反编译深入理解Java String及intern 原文传送门:http://www.cnblogs.com/paddix/p/5326863.html 一.字符串问题 字符串在我们平时的编码工作 ...
随机推荐
- zabbix-agentd;客户端开启多个端口。
学习标杆:https://access.redhat.com/documentation/zh-cn/red_hat_enterprise_linux/8/html/configuring_basic ...
- ACM对抗赛有感
2022.2.22 一个有"爱"的日子,注定不会平凡(对于24oier来说),原因是gg让我们参加与大连理工大学的对抗赛. 为此队友都准备好各种板子,上了比赛才发现根本没有 可怜了 ...
- 已经安装的nginx增加额外配置步骤
这里以安装第三方ngx_http_google_filter_module模块为例nginx的模块是需要重新编译nginx,而不是像apache一样配置文件引用.so1. 下载第三方扩展模块ngx_h ...
- 防世界之Web_NewsCenter
题目: 打开实验环境一看,就一个搜索框,emmm试下有没有SQL注入点,SQL注入步骤传送门https://www.cnblogs.com/shacker/p/15917173.html 爆出数据, ...
- Vulhub-漏洞环境的搭建(详细版)
安装Vulhub需要的基础环境 更新现有的软件 复制代码 1 2 sudo apt-get update sudo apt-get upgrade 安装Docker 复制代码 1 2 3 4 5 6 ...
- 8款国内外主流商业智能BI工具分析,助你轻松选型!
BI(Business Intelligence),即商业智能或商务智能,它是一套完整的解决方案,用来将企业中现有的数据进行有效的整合,快速准确的提供报表并提出决策依据,帮助企业做出明智的业务经营决策 ...
- WPF 开发,优化 AvalonEdit 显示单行超长文本的性能。
路遥工具箱内置了一个名为"JSON 格式化"的功能.通过该功能可以将 JSON 字符串进行美化和简化(设置缩进和取消缩进). 该功能上线后收到很多客户的反馈:大部分经由网络传输的J ...
- C语言中puts()和printf()区别
puts的功能更加单一,只能输出字符串:printf的功能更加广,可以格式化数据,输出多种类型的数据. puts()函数用来向标准输出设备(屏幕)写字符串并换行. 调用方式为puts(string): ...
- linux时钟校准
## 查看系统时间 date ## 查看硬件时间 hwclock ## 手动设置时间 date -s "20210507 17:55:00" ## 同步硬件时间 hwclock - ...
- random_sample() takes at most 1 positional argument (2 given)
是random模块下的sample函数,而不是np.random.