实话说,作为一个多年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 编码相关的更多相关文章

  1. Java String的相关性质分析

    引言 String可以说是在Java开发中必不可缺的一种类,String容易忽略的细节也很多,对String的了解程度也反映了一个Java程序员的基本功.下面就由一个面试题来引出对String的剖析. ...

  2. java String编码转换

    /** * Get XML String of utf-8 * * @return XML-Formed string */ public static String getUTF8XMLString ...

  3. Java String类相关知识梳理(含字符串常量池(String Pool)知识)

    目录 1. String类是什么 1.1 定义 1.2 类结构 1.3 所在的包 2. String类的底层数据结构 3. 关于 intern() 方法(重点) 3.1 作用 3.2 字符串常量池(S ...

  4. 面试话痨(二)C:JAVA String,别以为你穿个马甲我就不认识你了

    面试话痨系列是从技术广度的角度去回答面试官提的问题,适合萌新观看!   面试官,别再问我火箭怎么造了,我知道螺丝的四种拧法,你想听吗? String相关的题目,是面试中经常考察的点,当面试中遇到了St ...

  5. 从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念

    转(http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html#0-tsina-1-10971-397232819ff9a ...

  6. 手写代码 - java.lang.String/StringBuilder 相关

    语言:Java 9-截取某个区间的string /** * Returns a string that is a substring of this string. The * substring b ...

  7. java 中String编码和byte 解码总结——字节流和字符流

    1.InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符 InputStreamReader(InputStream in, Strin ...

  8. Java总结篇系列:Java String

    String作为Java中最常用的引用类型,相对来说基本上都比较熟悉,无论在平时的编码过程中还是在笔试面试中,String都很受到青睐,然而,在使用String过程中,又有较多需要注意的细节之处. 1 ...

  9. 通过反编译深入理解Java String及intern(转)

    通过反编译深入理解Java String及intern 原文传送门:http://www.cnblogs.com/paddix/p/5326863.html 一.字符串问题 字符串在我们平时的编码工作 ...

随机推荐

  1. 关于mybatis,需要掌握的基础

    目录 ❀ 总结 mybatis,需要掌握的基础如下: 1.了解ORM 思想.ORM思想的作用.映射配置的两种方式 2.MyBatis开发流程(基本使用) 3.日志框架 4.了解mybatis生命周期并 ...

  2. OpenHarmony移植:如何适配utils子系统之KV存储部件

    摘要:本文介绍移植开发板时如何适配utils子系统之KV存储部件,并介绍相关的运行机制原理. 本文分享自华为云社区<OpenHarmony移植案例与原理 - utils子系统之KV存储部件> ...

  3. 在k8s中使用性能分析神器:arthas

    Arthas(阿尔萨斯)是阿里巴巴开源的性能分析神器. k8s中使用arthas的三种方式 [bak]https://www.cnblogs.com/uncleyong/p/15498842.html ...

  4. [旧][Android] ButterKnifeProcessor 工作流程分析

    备注 原发表于2016.05.21,资料已过时,仅作备份,谨慎参考 前言 在 [Android] ButterKnife 浅析 中,我们了解了 ButterKnife 的用法,比较简单. 本次文章我们 ...

  5. 国内商业智能软件的天下来了,选择BI工具我们应该看这3点!

    ​数据变革时代,经常有"外行人"问到,何谓商业智能BI? BI可以说是通过软件或者服务,将网络时代中海量的数据转化为行动上的洞察力,从而影响企业的战略和战术决策. 当海量.高增长率 ...

  6. 【01】Spring Boot配置文件相关

    1.Spring Boot 获取属性的属性源,优先级从高到低 (1)命令行参数 (2)java:comp/env里的JNDI属性 (3)JVM系统属性 (4)操作系统的环境变量 (5)随机生成的的带r ...

  7. 【C# .Net GC】GC初始化设置 和GcSetting

    相关的类 GcSetting 类 GCLargeObjectHeapCompactionMode 枚举 GCLargeObjectHeapCompactionMode 枚举 属性的值 GCSettin ...

  8. Java -- next()和nextLine()的区别

    next()读取有效字符串,遇到Tab.空格.回车结束,所以不能接收带空格的字符串 nextLine()读取一行,只是以回车结束,所以可以接收带空格的字符串 https://blog.csdn.net ...

  9. 用RecyclerView实现水平滚动和网格视图

    建立RecyclerViewActivity.java文件 1 public class RecyclerViewActivity extends AppCompatActivity { 2 priv ...

  10. 01_opencv_python_基本图像处理

    1  图像基本操作   1.0.1  环境配置地址: Anaconda:https://www.anaconda.com/download/ Python_whl:https://www.lfd.uc ...