面试之Java String 编码相关

另有一篇我的字符编码本质入门的文章见这里:https://www.cnblogs.com/uncleguo/p/16008551.html

实话说,作为一个多年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]

我只能回答成这样了,自我感觉比较风流倜傥,潇洒惆怅的可以先自己琢磨下, 实际的程序输出在这里↓

面试之Java String 编码相关
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.

Original: https://www.cnblogs.com/uncleguo/p/16076173.html
Author: 锅叔
Title: 面试之Java String 编码相关

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/607299/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Linux 进程卡住了怎么办?

    在我们使用 Linux 系统时,如果网络或者磁盘等 I/O 出问题,会发现进程卡住了,即使用 kill -9 也无法杀掉进程,很多常用的调试工具,比如 strace, pstack…

    Linux 2023年5月27日
    094
  • 在vue中使用websocket回调函数中调用其他methods函数报错Uncaught TypeError: this.getHistory is not a function at WebSocket.ws.onmess解决方案

    1、报错详情 在vue中,使用了websocket类,在websocket的onmessage回调函数中调用methods的 getHistory 函数,发现一调用就报错 Unca…

    Linux 2023年6月8日
    099
  • java内存调优总结

    ···bashJVM 调优,是个很简单也很复杂的话题,由于经常遇到这类问题,在这里总结一下。 先从解决bug开始,当Java程序申请内存,超出VM可分配内纯的时候,VM首先可能会G…

    Linux 2023年6月14日
    0104
  • 经典45个git使用技巧与场合,专治不会合代码。

    前言 git对于大家应该都不太陌生,熟练使用git已经成为程序员的一项基本技能,尽管在工作中有诸如 Sourcetree这样牛X的客户端工具,使得合并代码变的很方便。但找工作面试和…

    Linux 2023年6月13日
    0135
  • 白话linux操作系统原理

    虽然计算机相关专业,操作系统和计算机组成原理是必修课。但是大学时和真正从事相关专业工作之后,对于知识的认知自然会发生变化。还很有可能,一辈子呆在学校的老师们只是照本宣科,自己的理解…

    Linux 2023年5月27日
    0126
  • 关于博客的解读

    写在前面 大家好,这里是满满! 最近也是想来写博客,我们学习过后的知识一旦过一段时间后,自然会有一些只是会遗忘,这时候大家都会去翻看以前的视频 ,笔记,遇到难一点的问题,可能就会去…

    Linux 2023年6月14日
    0120
  • 读软件测试艺术感悟

    断断续续的看,总觉得好像是漏掉了什么,而且一直 想着还有什么事情未做,却静不下心来去细品 开篇用自测评价测试,举例三角形例子写出等价类边界值的方法给测试人员举一个例子,更好的使测试…

    Linux 2023年6月8日
    0108
  • 记一次PowerShell配合Metersploit的艰难提权

    0x01 环境准备 kali(模拟公网攻击机) Windows2008(靶机,装有360、火绒、安全狗、D盾) Powersploit(PowerShell攻击框架) https:…

    Linux 2023年5月28日
    089
  • MapReduce入门实例——WordCount

    摘要:MapReduce的IDEA配置及WordCount案例 Maven项目配置 创建一个空的Maven项目 打开根目录下的 pom.xml文件,参考配置: UTF-8 3.2….

    Linux 2023年6月8日
    0107
  • MacOS设置终端代理

    前言 国内的开发者或多或少都会因为网络而烦恼,因为一些特殊原因有时候网络不好的时候需要使用代理才能完成对应的操作。原来我一直都是使用斐讯路由器然后刷了梅林的固件,直接在路由器层面设…

    Linux 2023年6月14日
    0104
  • Sqlite_入门命令

    新建库 .open DATA_BASE;新建表 create table LIST_NAME(DATA);语法: NAME 关键字… 新建数据 insert into …

    Linux 2023年6月7日
    079
  • nslookup:command not found的解决办法

    nslookup:command not found的解决办法 通过nslookup查看DNS记录,在这里遇到了一个小插曲,nslookup:command not found(未…

    Linux 2023年6月7日
    089
  • 什么是守护进程?

    在了解守护进程之前,需要先知道什么是什么是终端?什么是作业?什么是进程组?什么是会话? 在 Linux 中, 每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都…

    Linux 2023年6月8日
    0131
  • 解决USB在虚拟机不显示问题

    电脑重装了下系统,顺带重新装了vmware,发现虚拟机无法识别USB设备,居然连右下角图标和可识别设备都不显示了。 网上找了很多方法,大多是纷繁复杂,且行不行都无法保证。 我发现一…

    Linux 2023年5月27日
    0130
  • Netty-如何写一个Http服务器

    前言 动机 最近在学习Netty框架,发现Netty是支持Http协议的。加上以前看过Spring-MVC的源码,就想着二者能不能结合一下,整一个简易的web框架(PS:其实不是整…

    Linux 2023年6月7日
    0109
  • 一、Linux系统的简介与历史发展

    1.操作系统的介绍内核负责控制硬件资源分配,而如果只有内核,则只能让计算机硬件运行,而不能有任何功能,因此需要系统调用提供给开发者使用,从而开发应用程序;内核能够控制硬件,比如:让…

    Linux 2023年6月7日
    0157
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球