String.length()返回UTF-16代码单元数,非字符数或字节数;含增补字符时值为2但视觉上为1;需字符数用codePointCount(),字节数用getBytes(UTF_8),截取用offsetByCodePoints()。

Java 中字符串长度计算的“坑”,本质是混淆了三个不同维度的“长度”:字符个数(人眼可见)、UTF-16 代码单元数、字节数。编码差异只是放大了这个混淆,真正要避的是概念错位。
分清 length() 到底返回什么
String.length() 返回的是 UTF-16 代码单元(char)的数量,不是 Unicode 字符数,更不是字节数。
- 普通英文、常用汉字(BMP 区):一个字符占 1 个 char,length() 和“视觉字符数”一致
- emoji(如 ??)、生僻汉字(如 ? U+20213):属于 Unicode 增补平面,用一对代理 char(high + low surrogate)表示,length() 返回 2,但实际是 1 个字符
- 空字符串、纯 ASCII 字符串:length() 和字节数在 UTF-8 下碰巧相等,但这只是巧合,不可依赖
需要真实“字符数”时别用 length()
当业务逻辑按“用户看到几个字”判断(如“最多输入 10 个字”),必须统计 Unicode code point 数量:
- 用 str.codePointCount(0, str.length()) —— 这是最直接、最安全的方式
- 避免手写循环判断 isHighSurrogate / isLowSurrogate,除非项目卡在 JDK 7 及更早
- 注意:codePointCount 的参数仍是 char 索引范围,不是 code point 索引,所以第二个参数始终传 str.length()
涉及存储或传输限制时必须算字节数
数据库字段有字节上限(如 MySQL VARCHAR(255) 是字节限制)、HTTP header 有大小限制、日志系统按字节截断——这时 length() 完全无效:
立即学习“Java免费学习笔记(深入)”;
- UTF-8 字节数:str.getBytes(StandardCharsets.UTF_8).length
- GBK 字节数:str.getBytes(Charset.forName("GBK")).length
- 绝对不要用无参 getBytes(),它依赖系统默认编码,跨环境必出错
截取前 N 个“视觉字符”不能直接 substring
str.substring(0, 10) 按 char 索引切,可能把一个 emoji 的 high surrogate 和 low surrogate 拆开,导致末尾显示 或运行时报错:
- 先算安全结束位置:int end = str.offsetByCodePoints(0, Math.min(n, str.codePointCount(0, str.length())));
- 再截取:str.substring(0, end)
- 这个组合能确保不切断任何代理对,也不拆开组合 emoji(如 ??)


















