JAVA中的char只有两个字节,如何存下中文?

先运行下面代码

public class Test {
    public static void main(String[] args) {
        char c2 = 'a';
        System.out.println("\"a\"字的10进制整数" + (int)c2);
        System.out.println("\"a\"字的16进制整数" + Integer.toHexString(c2));
        String s2 = "a";
        byte[] s2UTF8Bytes = s2.getBytes(StandardCharsets.UTF_8);
        System.out.println("\"a\"字的UTF_8编码的字节长度:"+s2UTF8Bytes.length);
        for (int i = 0; i < s2UTF8Bytes.length; i++) {
            System.out.println("\"a\"字的UTF_8编码的第"+i+"字节为:"+Integer.toHexString(s2UTF8Bytes[i] & 0xFF));
        }

        char c1 = '丁';
        System.out.println("\"丁\"字的10进制整数" + (int)c1);
        System.out.println("\"丁\"字的16进制整数" + Integer.toHexString(c1));
        String s1 = "丁";
        byte[] s1UTF8Bytes = s1.getBytes(StandardCharsets.UTF_8);
        System.out.println("\"丁\"字的UTF_8编码的字节长度:"+s1UTF8Bytes.length);
        for (int i = 0; i < s1UTF8Bytes.length; i++) {
            System.out.println("\"丁\"字的UTF_8编码的第"+i+"字节为:"+Integer.toHexString(s1UTF8Bytes[i] & 0xFF));
        }
    }
}

运行结果

"a"字的10进制整数97
"a"字的16进制整数61
"a"字的UTF_8编码的字节长度:1
"a"字的UTF_8编码的第0字节为:61
"丁"字的10进制整数19969
"丁"字的16进制整数4e01
"丁"字的UTF_8编码的字节长度:3
"丁"字的UTF_8编码的第0字节为:e4
"丁"字的UTF_8编码的第1字节为:b8
"丁"字的UTF_8编码的第2字节为:81

由运行结果引出几个问题:

  1. a和丁是如何转为数字的?
  2. a和丁的数字97、61、19969、4e01、e4b881,他们是如何对应的?
  3. a的UTF8编码为1字节、丁的UTF8为3字节,如何存入容量是2字节的char类型里面的?

第一个问题很好回答,因为无论是中文字符还是英文字符,甚至其他的生僻字符,都是是以二进制形式存在计算机内存中的,像是01100001这样的,对应的就是十进制的97。

第二个问题,这些文字字符和数字不是随机对应的,是有固定的对应关系的,在java中使用的这个对应关系就是Unicode字符集。

  • Unicode是一个字符集,它为世界上几乎所有的字符分配了一个唯一的数值,即码点(code point)。
  • Unicode的目标是提供一个全球通用的字符编码,以涵盖世界上所有的文字系统。
  • Unicode的码点通常用十六进制表示,例如,中文字符“啊”在Unicode中的码点是U+554A。

查询Unicode码表

a对应的Unicode编号为U+0061,这是十六进制的,换算为十进制就是97。

丁对应的Unicode编号为U+4E01,十进制为19969

到此,a对应的十进制97、十六进制61 和 丁对应的十进制、十六进制4e01都有了解释,在来看UTF8编码。

UTF-8(8位元,Universal Character Set/Unicode Transformation Format)是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。因此,它逐渐成为电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。Unicode只是一组字符设定或者说是从数字和字符之间的逻辑映射的概念,但是它并没有指定代码点如何在计算机上存储,Unicode的编码有:UTF-8、UTF-16、UTF-32,UTF后的数字代表编码的最小单位,如UTF-8表示最小单位是8位,即1字节。

下面是UTF-8的编码规则

字节格式实际编码位码点范围
1字节0xxxxxxx70 ~ 127
2字节110xxxxx 10xxxxxx11128 ~ 2047
3字节1110xxxx 10xxxxxx 10xxxxxx162048 ~ 65535
4字节11110xxx 10xxxxxx 10xxxxxx 10xxxxxx2165536 ~ 2097151

表格中的x就是用unicode对应的二进制进行替换填充。

a的unicode十进制是97,对应码点范围是0~127,只有1字节,

UTF-8的二进制为01100001,十六进制就还是61

丁的unicode十进制是19969,对应码点范围是2048~65535,有3字节,

UTF-8的二进制为11100100 10111000 10000001,十六进制就是 E4 B8 81

其实在前面提到的Unicode码表中已经给出了对应的编码

最后一个问题,char如何存进3字节的中文的?

答案是char存的是UTF-16BE的编码,这个编码的中文是2字节的,见上图。

Unicode是有超过2字节的码点的,这些码点都无法装入java的char类型中,如:

char cc = 😀;

上面的代码是会报错的,

补充:

前面的代码中有这样一段:

Integer.toHexString(s2UTF8Bytes[i] & 0xFF));

为什么要对字符串转为的字节做与运算?

这是因为内存中的二进制是以补码的形式存在的,而Integer.toBinaryString 和 Integer.toHexString 方法都是直接把内存中二进制的给出来,丁的UTF8二进制:11100100 10111000 10000001,系统都会认为是补码,再由byte(11100100 )转为int(11111111111111111111111111100100),8位填充到32位的时候,负数的高位都是补1,结果就是等于了-28,所以需要通过 & 0xFF 运算消除高位多余的1,转为int(00000000000000000000000011100100),对应十进制228 和 十六进制E4,才能获得原本二进制对应的数字。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值