Java char类型介绍

前言:最近,想写一篇关于介绍产生”乱码问题“根本原因的文章,因此,查看了Java中的字符是如何存储的,即char数据类型。在此将学到的知识做一个总结。

一、char数据类型 

char类型最初用于表示Unicode字符集中的一个字符,但是随着Unicode标准的不断发展,其字符集不断扩展,表示的字符随之增加,已经超出了16位的char类型可以表示的范围(65535),现今,char类型用于表示一个代码单元。有些Unicode字符需要使用一个代码单元表示,有些Unicdoe字符需要两个代码单元表示。关于代码单元的概念,下文会详细介绍,这里先有一个印象即可。

1. 字面量值

字面量值需要单引号''括起来,单引号中必须有值,其形式如下:

  • 单个Unicode字符,最常见的形式,例如,'A''中'
  • 转义序列\u,范围\u0000~\uFFFF,例如, '\u2122' 表示字符'\u03C0'表示字符'π'
  • 特殊字符的转义序列,例如'\n'表示换行,'\''表示单引号。

转义字符,一般是不可打印的或者与语言的语法字符产生了冲突。

二、Unicode字符集

1. 字符集和编码规则

我们经常会听到Unicode、UTF-8、UTF-16这些术语,然而,它们是完全不同的概念。Unicode是字符集,UTF-8、UTF-16是编码规则,具体概念如下:

字符集:为每一个「字符」分配了一个唯一的 ID或者编号(称为为码位 / 码点 Code Point)
编码规则:将「编号」转换为字节序列的规则

 例如,‘中’ 的码点如下

Java char类型介绍_第1张图片

 其使用不同编码规则,进行编码的结果如下

Java char类型介绍_第2张图片

2. Unicode字符集


Java设计之初Unicode字符集中的字符个数还不到65536的一半,所以使用16位的char类型完全可以表示所有字符,但是,随着中、日、韩等其它字符的加入,导致Unicode字符集中的字符个数超过了65535,此时char类型已经无法表示Unicode字符集中的所有字符了。

我们先介绍一些必备的基础知识。

码点表示了一个字符的ID或者编号,在Unicode标准中,采用十六进制书写,并加上前缀U+,例如U+0041(转换为十进制即65),表示字母A的码点。

把Unicoee字符分为17个平面,每个平面包含不同种类的字符。

基本的多语言平面,码点范围U+0000~U+FFFF,表示了我们各个国家常用的字符,也是开始的char类型可以表示的字符。

其他平面(1到16号平面),码点范围U+10000~U+10FFFF,表示辅助字符,例如我们的不常用的繁体字等等,char类型无法直接表示这些字符,例如,char = '‘’ 会编译报错( ,一个数学符号,Unicdoe字符码点 U+1D546)

Java char类型介绍_第3张图片
那么,char如何表示码点在U+10000~U+10FFFF之间的Unicode字符呢?

它借鉴了UTF-16编码规则。

UTF-16使用不同长度的编码表示所有Unicode字符。在基本的多语言平面中,使用16位表示一个字符,称为代码单元,而其他平面的辅助字符需要使用32位,也就是一对代码单元表示一个字符。

U+D800~U+DBFF表示第一个代码单元,U+DC00~U+DFFF表示第二个代码单元。

U+D800~U+DFFF,这一段码点称为替换区域,可以看出它属于基本的多语言平面(U+0000~U+FFFF)的范围,为了避免产生歧义或冲突,替换区域的码点没有没配给任何字符。

假设为替换区域码点分配了字符,就无法判断该码点是表示一个字符,还是辅助字符的第一个代码单元或第二个代码单元。

具体让我们看一下如何表示字符 ‘⑪’ (Unicdoe字符码点 U+1D546)?

第一个代码单元:D800到DBFF
1101 1000 0000 0000
1101 1011 1111 1111
110110XXXXXXXXXX 可以编码10位二进制数

第二个代码单元:DC00到DFFF
1101 1100 0000 0000
1101 1111 1111 1111
110111XXXXXXXXXX 可以编码10位二进制数

U+10000到U+10FFFF 可以看出转换成二进制树最多有21位,但是我们加起来最多可以编码20位二进制数,怎么办?

将范围偏移-10000,范围变成了U+0000到U+FFFFF,如此,就满足20位编码了。

⑪ U+1D546 偏移后U+D546

1101 0101 0100 0110

后十位“01 0100 0110"放入第二个代码单元110111XXXXXXXXXX:1101 1101 0100 0110,对应的十六进制数:DD46

前十位(不足十位首部补0)00001101 01 放入第一个代码单元110110XXXXXXXXXX:1101 1000 0011 0101,对应的十六进制数是:D835

因此,⑪,U+1D546使用两个代码单元表示:D835 DD46

我们在程序中如何存储码点在U+10000到U+10FFFF之间的字符呢?

直接存储字符字面量值或者转义序列\u,编译报错。很容易理解char类型16位正好只能存储一个代码单元,而该字符需要两个代码单元表示,无法用char类型直接表示该字符。

Java char类型介绍_第4张图片

总结:现在,char类型用于描述一个代码单元对于基本的多语言平面中的字符(码点范围U+0000到U+FFFF,不包含替换区域)可以使用一个代码单元表示,也就是一个char值。对于其他平面的字符(码点范围U+10000到U+10FFFF)可以使用两个代码单元表示,也就是两个char值。

严格来说,char类型可以表示使用一个代码单元表示的Unicode字符,char类型无法直接表示两个代码单元表示的字符。

怎么办,对于需要两个代码单元表示的字符就不能使用了吗?
不是的,我们可以把他们放在字符串类型中,字符串是由字符构成的,它把U+D800到U+DBFF之间的代码单元解读为字符的第一个代码单元,把U+DC00到U+DFFFF解读为字符的第二个代码单元,然后把两个代码单元解读为一个字符。它把U+0000到U+FFFF之间除去替换区域(U+D800到U +DFFF)的代码单元解读为一个字符。

String str = "\uD835\uDD46";


最后的建议,程序中最好不要使用char类型,除非你想处理代码单元。像操作Unicode字符,可以使用Java中的String类型。

 public static void main(String[] args) {

        // 字符串中放入需要两个码点表示的字符''
        String  str = "A";

        // 获取索引为0的代码单元,而不是字符
        char ch0 = str.charAt(0);
        char ch1 = str.charAt(1);
        char ch2 = str.charAt(2);

        System.out.println(ch0);
        System.out.println(ch1);
        System.out.println(ch2);

    }

有些字符不能被正确的处理


 

你可能感兴趣的:(java,开发语言)