在Java中,一个char类型变量大多数情况下可以存储一个字符,但是有的时候却不可以。要想搞清楚原因,就需要了解到字符集、字符编码、码点和代码单元等等概念。本文将逐一解释这些概念。

什么是字符集

我们日常生活中所使用的文字,在存储到计算机中的时候需要使用二进制的形式,通俗地来说就是一个字符要唯一对应一个数字。字符集就用来表达这种对应关系的。这个世界上有很多的字符集,比如说简单的ASCII、国家标准总局发布的GB2312与GB18030以及还在不断发展的Unicode。

java的char的数字 java中char怎么用_ico

Unicode包含了世界上大多数的文字和符号。我们平常聊天时常用的表情符号 – Emoji,就已经被Unicode标准化了,每个表情就代表了一个“文字”。Unicode中,每个字符对应的数字,叫代码值(又叫Code Point, 码点),通常会以”U+”开头,后面跟上一串十六进制数字。比如U+86CB所表示的文字就是中文里的”蛋”。

什么是字符编码

上面我们说了,一个数字代表一个字符,那么这个数字怎么存储,就需要字符编码来进行规范了。我们拿Unicode字符集的几种编码UTF-8、UTF-16、UTF-32来说明不同的字符编码有什么优缺点,如何进行取舍。

java的char的数字 java中char怎么用_ico_02

UTF-8的格式

UTF-8是一种变长的编码方式,一个字符可能由1到4个字节来表示,比如英文字母在UTF-8一个字节就可以表示,常用的字符大都可以用2-3个字节表示,IETF要求所有互联网协议都必须支持UTF-8。UTF-8兼容ASCII码,纯ASCII码的字符串也是一个合法的UTF-8字符串。

UTF-16用1个或者2个代码单元(Code Unit)来表示字符,一个代码单元占用2个字节,大部分常用字符用1个代码单元就可以。Java语言内部使用UTF-16。

UTF-32中每个字符所用的空间都是一样的,4个字节,有点浪费空间。不过他可以方便地从编码序列里以常数时间找出第n个字符。

java的char的数字 java中char怎么用_ico_03

UTF-8在网页里的使用率

对于英文或者其他欧洲语言,使用UTF-8会比UTF-16占用更少的空间。使用中文或者日文更多的话,使用UTF-16会更划算。像HTML这种充满了标签的文档,使用UTF-8会减少很多空间占用。另外,UTF-8的冗余度更大,在传输过程中个别bit丢失,仍可以识别后续的字符,UTF-16就不行。

Java为啥用UTF-16?

早些时候,Unicode还没有这么多字符,Java设计之初考虑到了这一点,就用2个字节大小的char类型来表示所有字符。当时用的应该是UCS-2编码,没想到好景不长,Unicode里字符数量马上爆炸了。

虽然Java内部人员有提议将char扩展为4个字节或者新增一个char32类型的(UTF-32),不过考虑到需要节省空间以及兼容性,没这么做。Java换成了UTF-16编码,他是UCS-2的超集。char类型用来表示上面提到的代码单元。

java的char的数字 java中char怎么用_java中输入char类型_04

b字符串有2个代码单元(一个码点)

也就是说一个字符串里,一个字符由一个或者两个char来表示。String.length()方法实际上返回的是字符串里代码单元的的数量。从这里可以看出char的使用其实是非常底层的,所以推荐大家在平常的代码中不要使用char,除非你已经完全理解了本文的内容。

如何获取码点(Code Point)

从Java 9开始,String提供了codePoints方法返回IntStream,可以获取字符串中的码点流,码点用整型表示。再此之前你可以用codePointCount()和codePointAt()方法。