String.codePointCount() 返回 Unicode 字符数(含代理对),而 length() 仅返回 UTF-16 编码单元数;例如 "?" 占 2 个 char 但算作 1 个 code point,故需 codePointCount() 获取语义长度。

直接用 String.codePointCount() 就能拿到字符串在 Unicode 层面的“真实字符数”,也就是把代理对(surrogate pair)当作一个字符来算,而不是简单按 char 数(即 UTF-16 编码单元数)。
为什么需要 codePointCount() 而不是 length()
String.length() 返回的是 UTF-16 编码单元个数,不是人类认知中的“字符个数”。比如 emoji 表情 ?、某些中文古字、或者带变体符号的组合字符(如 ??),可能占 2 个 char(即一个代理对),但实际是 1 个 Unicode 字符(code point)。codePointCount() 才反映这种语义上的“长度”。
基本用法:全字符串计数
最常用形式是统计整个字符串的 Unicode 字符总数:
String s = "Hello ? ??"; int len = s.codePointCount(0, s.length()); // 返回 9
- 第一个参数是起始索引(含),第二个是结束索引(不含),范围必须合法
- 等价于
s.codePointCount(0, s.length()),可封装成工具方法复用 - 注意:不能写成
s.codePointCount(0, s.length() + 1),会抛IndexOutOfBoundsException
处理子串或带范围的计数
当你只关心某一段逻辑字符(比如前 5 个 Unicode 字符),可以用偏移量精准切分:
String text = "A\uFE0F\u20E3B"; // 带变体选择符的 A️⃣,共 1 个 visible character int cpLen = text.codePointCount(0, 3); // 从 0 到第 3 个 UTF-16 单元(含前 3 个编码单元) // 实际上这 3 个 char 构成 1 个 code point,所以返回 1
- 索引始终按 UTF-16 编码单元(
char)位置传入,不是按字符位置 - 要安全地“取前 N 个 Unicode 字符”,得配合
offsetByCodePoints()计算边界,不能直接用substring(N) - 例如:想截取前 2 个 Unicode 字符,应先调
s.offsetByCodePoints(0, 2)得到结束位置,再substring(0, end)
和其它方法对比:避免常见误区
别混淆这几个概念:
-
s.length()→ UTF-16 编码单元数(可能高估“字符”数量) -
s.codePointCount(0, s.length())→ Unicode 字符数(推荐用于“长度感知”场景) -
s.codePoints().count()→ 效果相同,但创建了 IntStream,有额外开销,适合链式操作而非单纯计数 -
s.chars().count()→ 等同于length(),只是按int拆char,不识别代理对

















