大聪明教你学Java | 一个有趣的问题:一个字符的 String.length() 是多少

11,514 阅读5分钟

前言

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情

🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。

🍊支持作者: 点赞👍、关注💖、留言💌~

看到这个标题的时候估计有小伙伴会说:一个字符的 String.length() 肯定是1呗,那还能是多少... 但是事实真的是这样吗😉....

一探究竟

咱们先举个例子👇

/**
 * @description: Demo
 * @author: 庄霸.liziye
 * @create: 2022-02-11 09:23
 **/
public class Demo {
    public static void main(String [] args){
        String message = "我在人民广场吃着炸鸡";
        System.out.println("message的长度是:" + message.length());
    }
}

各位小伙伴肯定一眼就能看出来结果是多少,没错~ 打印出来的长度是10💯那么我把上面的代码修改一下,大家再看看结果是多少呢👇

/**
 * @description: Demo
 * @author: 庄霸.liziye
 * @create: 2022-02-11 09:23
 **/
public class Demo {
    public static void main(String [] args){
        String message = "我在人民广场吃着炸鸡𝄞";
        System.out.println("message的长度是:" + message.length());
    }
}

估计会有小伙伴脱口而出:这不就是多了一个符号嘛,结果肯定是11,这可难不倒我😄。那咱们一起来看看控制台打印出来的结果是多少~

在这里插入图片描述 控制台打印出来的结果居然是12 😮,也就是说 𝄞 字符的长度是2。这个结果就有点出乎意料了,我们在开发的时候肯定也计算过特殊字符(比如感叹号、问号、逗号等等)的长度,这些字符的长度都是1,那为什么 𝄞 的长度就变成了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;
    }

把注释翻译一下,大概的意思是:返回此字符串的长度,返回的长度等于字符串中的 unicode 代码单元数。那 “unicode 代码单元数” 又是什么呢?咱们接着往下看👇

在 Java 中有内码和外码的区分,简单来说内码就是 char 或 String 在内存里使用的编码方式;除内码外的编码方式都可以认为是外码(class 文件的编码也可以认为是外码)。而 Java 内码,也就是 unicode 中使用的是 UTF-16 编码方式。那么我们把上面的翻译再做进一步的解释就是:返回字符串的长度,这一长度等于字符串中的 UTF-16 的代码单元的数目。

代码单元(Code Unit)指的是一种转换格式(UTF)中最小的一个分隔,因此,一种转换格式只会包含整数个单元(UTF-X 中的数字 X 就是各自代码单元的位数)。UTF-16 的 16 指的就是最小为 16 位一个单元,即两字节为一个单元,UTF-16 可以包含一个单元和两个单元,对应即是两个字节和四个字节。我们操作 UTF-16 时就是以它的一个单元为基本单位的。UTF-16编码一个字符对 U+0000 - U+FFFF 范围内的字符采用2字节进行编码,而超过这个范围(大于 U+FFFF)的字符则采用四字节进行编码,前者是两字节也就是一个代码单元,后者一个字符是四字节也就是两个代码单元。说到这各位小伙伴肯定就明白了,因为 𝄞 字符的 Unicode 值大于 U+FFFF ,所以该字符在编码时用了四个字节,也就是两个代码单元,这也就是为什么 𝄞 字符的长度会是2。

讲到这或许有小伙伴会提出疑问:那我以后再用 String.length() 方法去计算字符串长度是不是就会出现问题了呢?其实完全不用担心,这种情况是极少遇到的,我们常用的字符(比如感叹号、问号、句号、逗号等等)Unicode 值都在 U+0000 - U+FFFF 范围内,我们是可以直接使用 String.length() 方法去计算其长度的,完全不用担心出现问题。即便我们真的遇到了这种极其特殊的字符,我们可以使用 String.codePointCount(int beginIndex, int endIndex) 方法去计算字符长度👇

/**
 * @description: Demo
 * @author: 庄霸.liziye
 * @create: 2022-02-11 09:23
 **/
public class Demo {
    public static void main(String [] args){
        String message = "𝄞";
        System.out.println(message.length());
        System.out.println(message.codePointCount(0,message.length()));
    }
}

在这里插入图片描述 可能有些小伙伴没用过 String.codePointCount(int beginIndex, int endIndex) 方法,这里再简单说解释一下👇

UTF-16 是基于 Unicode 的不定长度的字符编码(不定长度是指,有些字符使用 2 个字节表示,有些字符使用 4 个字节表示)。UTF-16 的基本单位是 2 个字节,英文名叫 code unit,而真正能表示一个 Unicode 字符的数据(在 utf-16 中可能是 2 个字节也可能是 4 个字节)叫代码点,英文名叫 code point。 比如 "𝕆" 这个字符的 Unicode 码(或者叫代码点)是 U+1D546(超过了 上文中提到的编码范围),转换成 UTF-16 后就有两个代码单元,分别为 U+D835 和 U+DD46,此时使用 String.length() 去计算其长度,结果就是2,而如果使用 String.codePointCount(int beginIndex, int endIndex) ,那么结果就为 1 。

总结起来就是一句话:length() 方法计算的是代码单元,codePointCount() 计算的是代码点(即真正的字符数)。

小结

本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇‍

希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●'◡'●)

如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。

爱你所爱 行你所行 听从你心 无问东西