面试之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)

大家都在看

  • NJU软件分析笔记(1)

    课程链接本次课程主要内容 Compilers and Static Analyzers AST vs. IR IR: Three-Address Code (3AC) 3AC in…

    Linux 2023年6月8日
    0112
  • 当保存参数使用结构体时必备的开发技巧方式

    1、前言 想必做嵌入式产品开发都遇到过设备需要保存参数,常用的方式就是按照结构体的方式管理参数,保存时将整个结构体数据保存在 Flash 中,方便下次读取。 1.1、目的 本文时分…

    Linux 2023年6月7日
    0106
  • 应用配置管理,基础原理分析

    工程可以有点小乱,但配置不能含糊; 一、配置架构 在微服务的代码工程中,配置管理是一项复杂的事情,即需要做好各个环境的配置隔离措施,还需要确保生产环境的配置安全;如果划分的微服务足…

    Linux 2023年6月14日
    0108
  • linux自动备份mysql数据库

    备份脚本记录一下–(单个数据库) 2021-11-15 1.新建shell脚本:vim **.sh #!/bin/bashCKUP=/data/backup/db #获…

    Linux 2023年5月27日
    0109
  • 小记:音频格式转化ByPython(上)

    近日新买了个耳机,店家附送了一些周董的无损音乐资源,收到货后迫不及待的下载试听,才发现这些资源是wav格式的,导入播放器后歌名、作者、专辑等全是未知,当时想着是不是店家的资源有问题…

    Linux 2023年6月8日
    0106
  • POJ3368(Frequent values)–线段树

    题目在这里3368Accepted 7312K 1829MSC++ 6936B 题意为给你一组数据,再给定一组区间,问你这个区间内出现次数最多的元素的次数是多少。 我还记得这题是学…

    Linux 2023年6月7日
    0108
  • Docker存储卷

    Docker存储卷 1、COW机制 Docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层。 如果运行中的容器修改了现有的一个已…

    Linux 2023年6月7日
    080
  • MySQL备份与恢复

    MySQL备份与恢复 备份是数据安全的最后一道防线,对于任何数据丢失的场景,备份虽然不一定能恢复百分之百的数据(取决于备份周期),但至少能将损失降到最低。 数据丢失的场景举例: 人…

    Linux 2023年6月7日
    0168
  • 关于如何在window下执行SQLSERVER的定时备份

    引言 在使用SqlServer Express 版本的时候发现,这个版本不支持通过数据库的代理方式进行数据库的维护。 解决方案 使用SQL语句加windows任务计划的方式解决具体…

    Linux 2023年6月14日
    090
  • Canal.adapter报错

    Canal.adapter报错 报错如下: 2021-09-09 15:56:33.669 [Thread-12] ERROR c.a.o.canal.adapter.launch…

    Linux 2023年6月8日
    0100
  • SpringSecurity

    SpringSecurity 11.1 SpringSecurity简介 Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spr…

    Linux 2023年6月14日
    094
  • Linux命令之find、grep、echo、tar、whoami、uname

    1. whoami–查看当前登录的用户名 book@100ask:~/linux$ whoami book 2. echo–打印命令,配合’&g…

    Linux 2023年6月6日
    096
  • 【持久层框架】- SpringData-JPA

    SpringData – JPA 😄生命不息,写作不止🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆 一个有梦有戏的人 @怒放吧德德🌝分享学习心得…

    Linux 2023年6月6日
    073
  • 2020年12月-第01阶段-前端基础-HTML CSS 项目阶段(二)

    品优购项目(二) 1. 品优购首页布局 命名集合:名称 说明 快捷导航栏 shortcut 头部 header 标志 logo 购物车 shopcar 搜索 search 热点词 …

    Linux 2023年6月8日
    090
  • 【证券从业】金融基础知识-第四章 股票02

    注1:后续学习并整理到第八章,全书完结后再合并成一个笔记进行源文件分享 注2:本章内容巨多,大约分为两篇文章记录消化 posted @2022-06-06 02:11 陈景中 阅读…

    Linux 2023年6月13日
    0104
  • redis批量删除key 远程批量删除key

    一、遇到的问题 在开发的过程中,经常会遇到要批量删除某种规则的key,如缓存的课程数据”course-课程uid”,其中课程uid是变量,我们需要删除&#8…

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