不建议在 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还有别的 ),建议少用而已。

相关文章
|
27天前
|
安全 Java
Java异常处理:程序世界的“交通规则
Java异常处理:程序世界的“交通规则
261 98
|
3月前
|
人工智能 监控 安全
智慧工地解决方案,java智慧工地程序代码
智慧工地系统融合物联网、AI、大数据等技术,实现对施工现场“人、机、料、法、环”的全面智能监控与管理,提升安全、效率与决策水平。
100 2
|
24天前
|
存储 Java Go
【Java】(3)8种基本数据类型的分析、数据类型转换规则、转义字符的列举
牢记类型转换规则在脑海中将编译和运行两个阶段分开,这是两个不同的阶段,不要弄混!
136 2
|
24天前
|
Java Go 开发工具
【Java】(2)Java数据类型阐述、基本数据类型的占用和范围、二进制的讲述
数据的一种表示形式。十进制表示满十进一原则。二进制表示满二进一原则。例如:十进制例如:二进制计算机在任何情况下都只能识别二进制。
65 1
|
2月前
|
安全 Java
使用Java多态性进行数据类型转换
以上示范了如何通过多态性来执行安全有效地对于Java对象间相互之间进行正确方式与场合下正确格式与条件限定内对于各个层级关系内部成员及其功能调取与应有操作处理过程,并通过 `instanceof`关键字确保了程序运行期间遵循逻辑合理并符合预设规则条件限定内正常工作流程而无需担忧潜藏风险问题出现可能带来影响结果质量问题情况存在可能。
75 12
|
2月前
|
存储 Java 编译器
对比Java学习Go——程序结构与变量
本节对比了Java与Go语言的基础结构,包括“Hello, World!”程序、代码组织方式、入口函数定义、基本数据类型及变量声明方式。Java强调严格的面向对象结构,所有代码需置于类中,入口方法需严格符合`public static void main(String[] args)`格式;而Go语言结构更简洁,使用包和函数组织代码,入口函数为`func main()`。两种语言在变量声明、常量定义、类型系统等方面也存在显著差异,体现了各自的设计哲学。
|
3月前
|
存储 缓存 算法
Java数据类型与运算符深度解析
本文深入解析Java中容易混淆的基础知识,包括八大基本数据类型(如int、Integer)、自动装箱与拆箱机制,以及运算符(如&与&&)的使用区别。通过代码示例剖析内存布局、取值范围及常见陷阱,帮助开发者写出更高效、健壮的代码,并附有面试高频问题解析,夯实基础。
|
4月前
|
存储 人工智能 JavaScript
Java中的数据类型转换
本文介绍了 Java 中常见的数据类型转换方法,涵盖字符串与整型、长整型、浮点型、布尔型及日期类型的相互转换,并讲解了可能出现的异常和处理方式。
190 0
|
7月前
|
存储 缓存 Java
理解Java引用数据类型:它们都是对象引用
本文深入探讨了Java中引用数据类型的本质及其相关特性。引用变量存储的是对象的内存地址而非对象本身,类似房子的地址而非房子本身。文章通过实例解析了引用赋值、比较(`==`与`equals()`的区别)以及包装类缓存机制等核心概念。此外,还介绍了Java引用类型的家族,包括类、接口、数组和枚举。理解这些内容有助于开发者避免常见错误,提升对Java内存模型的掌握,为高效编程奠定基础。
337 0
下一篇
开通oss服务