String.length() 返回 UTF-16 代码单元数而非用户感知字符数,因辅助平面 Emoji(如 ? U+1F30D)需代理对表示,占 2 个 char;应使用 codePointCount() 或图形单元切分获取真实字数。

Java 中 String.length() 方法返回的是字符串中 Unicode 代码单元(code units)的数量,而不是字符(grapheme clusters)个数。Emoji(尤其是较新的、组合型或修饰型 Emoji)通常由多个 Unicode 代码点(code points)组成,而 Java 的 String 内部使用 UTF-16 编码,一个代码点若超出基本多文种平面(BMP),就需要用两个 UTF-16 代码单元(即一个代理对,surrogate pair)来表示——这时 length() 就会返回 2。
为什么 Emoji 会占两个代码单元?
UTF-16 将 Unicode 码位(0x0000–0x10FFFF)分为:
- BMP 范围(U+0000 到 U+FFFF):每个字符占 1 个
char(即 16 位),length()返回 1; - 辅助平面(U+10000 及以上):如大部分 Emoji(例如 ? U+1F30D、?? U+1F469 U+200D U+1F4BB),需用两个
char表示(高代理 + 低代理),length()就返回 2 或更多。
常见“看似一个 Emoji 却 length=2 的例子
以下 Emoji 在 Java 中调用 .length() 都返回 2:
-
"?"(U+1F30D)→ 占 2 个char; -
"?"(U+1F44D)→ 占 2 个char; -
"??"(家庭/职业组合 Emoji)→ 实际是多个代码点(含 ZWJ 连接符),总length()可能达 4–5,但其中每个辅助平面字符本身已占 2。
如何获取真正的“字符数”(用户感知的字数)?
要用能识别 Unicode 标量值(code points)和扩展字形簇(extended grapheme clusters)的方式:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 用
String.codePointCount(0, str.length())获取代码点数量(更接近“逻辑字符数”,对单个 Emoji 如 ? 返回 1); - 对复杂 Emoji(如带肤色修饰符或 ZWJ 组合的 ?❤️??),需借助 ICU4J 或
java.text.BreakIterator按图形单元切分,才能准确计数; - 简单场景下,可先用
str.codePoints().count()(Java 8+ 流式 API)快速得到 code point 总数。
实际开发中要注意什么?
别依赖 length() 做显示截断、输入限制或索引遍历:
- 用
charAt(i)可能取到代理对的半边,导致乱码; - 截取前 10 个
char可能切开一个 Emoji,显示为 ; - 校验用户名长度、短信字数等业务逻辑时,应统一用
codePointCount或图形单元计数。
本质上,这不是 bug,而是 UTF-16 编码模型与现代 Unicode 字符复杂性之间的张力。理解这点,就能避开多数 Emoji 相关的长度误判问题。

















