string.length() 返回 char 个数,string.codepointcount() 返回 unicode 码点数;因 utf-16 中代理对(如 emoji、部分汉字)占 2 个 char,故二者结果可能不同。

为什么 String.length() 和 String.codePointCount() 结果可能不同
因为 Java 的 String 内部用 UTF-16 编码,而一个 Unicode 字符(比如 emoji 或某些汉字)可能占两个 char(即一个代理对,surrogate pair)。String.length() 返回的是 char 个数,不是真实字符数;String.codePointCount() 才按 Unicode 码点(code point)计数,能正确处理代理对。
怎么调用 String.codePointCount() 才不会出错
它必须传入合法的索引范围,否则抛 IndexOutOfBoundsException。常见误用是直接传 0 和 str.length() 而不检查字符串是否为空或含代理对。
- 始终用
str.codePointCount(0, str.length())—— 这是统计整串的标准写法 - 如果字符串为
null,先判空,否则 NPE - 起始索引必须 ≤ 结束索引,且都不能越界(
0 ≤ beginIndex ≤ endIndex ≤ str.length()) - 不要手动拆分字符串再计数,比如
str.substring().codePointCount(...),容易索引错位
哪些字符会导致 length() ≠ codePointCount()
所有码点 > 0xFFFF 的 Unicode 字符都会触发代理对机制,典型包括:
- 大部分 emoji:
"??"、"?"、"?" - 部分罕见汉字和符号:如
"?"(U+2070E)、"?"(U+2A6A5) - 增补平面(SMP)中的任意字符,码点范围
0x10000–0x10FFFF
验证示例:"?❤️??".length() 返回 11,但 "?❤️??".codePointCount(0, str.length()) 返回 1 —— 这是一个带多个 ZWJ 连接符的组合 emoji,仍算作单个 Unicode 字符(尽管底层由多个码点构成,但 codePointCount 统计的是基础码点数量,不解析组合规则)。
替代方案与兼容性注意点
codePointCount() 自 JDK 1.5 起可用,无兼容问题。但要注意它只解决“码点计数”,不解决“视觉字形(grapheme cluster)计数”——比如带变音符号的 "é"(e + ◌́)会被计为 2 个码点,但人眼视为 1 个字符。真要按用户感知计数,得用 java.text.BreakIterator。
另外,别混淆 str.chars().count()(返回 char 流长度,等价于 length())和 str.codePoints().count()(等价于 codePointCount(0, str.length())),后者更直观但有额外流开销。
真正容易被忽略的是:即使你知道用 codePointCount,也常忘记它对空字符串返回 0,对只含代理对前半部分的非法字符串会抛异常——所以生产代码里,只要输入不可控,就得加 try-catch 或预校验 Character.isSurrogatePair()。










