不建议在 Java 程序中使用 char 数据类型

简介: 有了Unicode 字符集后,我们要考虑的就是以什么样的方式对这些字符进行传输和存储,这就是 Unicode 编码的实现方式,我们称为 Unicode 转换格式(Unicode Transformation Format,简称 UTF)。我们熟悉的 UTF-8、 UTF-16 等就是不同的 Unicode编码实现方式。

好了,看了标题我知道你有疑问,这里我得承认算并半个标题党吧。


事情是这样的:


这里有段程序,你跑一下,结果可能跟你想的不一样


 public static void main(String[] args) {
    String str = "䕫";
    System.out.println(str.length());
  }


你可能认为字符串长度应该是1吧,为什么会是2呢?这里其实就是所谓的『坑』,说到这个坑,话就有些长了,我们先看一些关于字符的概念。


以下的基础知识我相信大多数开发的同学都知道,如果你明白直接跳过就好。



Unicode 字符集的出现就是为了统一编码所谓字符集就是一个由众多不同的字符组成的集合。


Unicode 字符集对每一个字符都分配了一个唯一的 代码点(code point) 用来标识字符本身。


所谓代码点就是一个添加了 U+ 前缀的十六进制整数,如字母 A 的代码点就是 U+0041。


有了Unicode 字符集后,我们要考虑的就是以什么样的方式对这些字符进行传输和存储,这就是 Unicode 编码的实现方式,我们称为 Unicode 转换格式(Unicode Transformation Format,简称 UTF)。我们熟悉的 UTF-8、 UTF-16 等就是不同的 Unicode编码实现方式


码点如何转换成UTF的几种形式呢?


32.jpg


如上图所示

  • UTF-32采用的定长四字节则是32位
  • UTF-8是变长的编码方案,可以有1,2,3,4四种字节组合
  • UTF-16是一种变长的2或4字节编码模式

在 Unicode 字符集诞生之初,采用 UCS-2(2-byte Universal Character Set) 这种定长的编码方式对 Unicode 字符集进行编码,这种方式采用 16 bit 的长度来进行字符编码,所以最多可以对 2^16 = 65536 个字符进行编码(编码范围从 U+0000 ~ U+FFFF)。在当时的情况下,设计者们用了不到一半的数量就对所有字符进行了编码,并且认为剩余的空间足够用于未来新增字符的编码。


不幸的是,随着中文、日文、韩文等表意文字不断的加入,Unicode 字符集中的字符数量很快超过了 16 位所能编码的最大字符数量,于是设计者们对 Unicode 字符集进行了新的设计。


新的设计将字符集中的所有字符分为 17 个 代码平面(code plane)。其中 U+0000 ~ U+FFFF 这个代码点范围被划定为 基本多语言平面(Basic MultilingualPlane,简记为 BMP,如下图第一个花花绿绿的那个),其余的字符分别划入 16 个 辅助平面(Supplementary Plane),代码点范围为 U+10000 ~ U+10FFFF,这些处于辅助平面的字符我们称作 增补字符(supplementary characters)。


33.jpg


在 Unicode 字符集中的字符被重新划分到不同平面后,需要注意:


BMP 范围内的字符和 UCS-2 下的字符编码基本保持一致,但是 BMP 中的 U+D800 ~ U+DFFF 部分被留空,不分配给任何字符,作用是用于给辅助平面内的字符进行编码。

不是每个平面内的每个位置都被分配给了指定的字符,原因是:特殊用途,如 BMP 中的 U+D800 ~ U+DFFF 部分;


  • 作为保留空间
  • 没有足够的字符


回答程序输出长度为2而不是1的问题


我们使用的字符其实不是普通字符,而是增补字符,我们知道 Java 中 char 的长度永远是 16 位,如果我们在字符串中使用了增补字符,那就意味着需要 2 个 char 类型的长度才能存储,对于 String 底层存储字符的数组 value 来说,就需要 2 个数组元素的位置。我们再看一下String 类length方法的源码:


/**
     * Returns the length of this string.
     * The length is equal to the number of <a href="Character.html#unicode">Unicode
     * code units</a> in the string.
     *
     * @return  the length of the sequence of characters represented by this
     *          object.
     */
public int length() {
return value.length;
    }


一切就明白了。java 的 String 内部用的 UTF-16 编码,String.length() 直接返回 code unit 的个数,也就是 Java 的 2 字节 char 的个数。


当然这里不是说绝对不要用char,只是坑多(上面只是其中一个,JDK9还有别的 ),建议少用而已。

相关文章
|
1天前
|
Java Linux C语言
一步带你了解java程序逻辑控制
一步带你了解java程序逻辑控制
8 2
|
1天前
|
Java 数据安全/隐私保护
java中程序控制的典例
java中程序控制的典例
7 1
|
2天前
|
存储 Java 数据库连接
使用Java开发桌面应用程序
使用Java开发桌面应用程序
8 0
|
2天前
|
关系型数据库 MySQL Java
通过使用阿里云服务器,搭建Java程序的运行环境
通过使用阿里云服务器,搭建Java程序的运行环境
|
3天前
|
存储 安全 Java
Java一分钟之Java数据类型概览:基本类型与引用类型
【5月更文挑战第7天】本文概述了Java中的基本和引用数据类型,强调了理解它们对高效编程的重要性。基本类型包括数值、布尔和字符类型,而引用类型涉及类、接口、数组等。注意基本类型的精度损失和溢出问题,以及引用类型的空指针异常和内存泄漏。通过明确类型范围、使用包装类、空值检查和及时释放资源来避免这些问题。代码示例展示了基本类型和引用类型的使用。理解这些核心概念有助于编写更健壮的Java代码。
11 1
|
9天前
|
存储 网络协议 Java
本地MinIO存储服务通过Java程序结合cpolar实现远程连接上传文件
本地MinIO存储服务通过Java程序结合cpolar实现远程连接上传文件
|
10天前
|
存储 Java 程序员
Java中的基本数据类型有哪些?
【4月更文挑战第30天】Java中的基本数据类型有哪些?
15 1
|
11天前
|
存储 Java 开发工具
【Java探索之旅】用面向对象的思维构建程序世界
【Java探索之旅】用面向对象的思维构建程序世界
10 0
|
11天前
|
Java
【Java探索之旅】数据类型与变量 字面常量 整型变量
【Java探索之旅】数据类型与变量 字面常量 整型变量
22 0
|
11天前
|
存储 Java C语言
【Java探索之旅】数据类型与变量 浮点型,字符型,布尔型,字符串型
【Java探索之旅】数据类型与变量 浮点型,字符型,布尔型,字符串型
19 0