String.codePoints()返回int型码点流,解决char无法表示增补字符的问题;chars()按UTF-16单元拆分会导致emoji等被误判;遍历时不可强转char,应使用Character.toChars()。

String.codePoints() 返回的是 int 类型的码点流,不是 char
Java 的 char 是 16 位,只能表示 BMP(基本多文种平面)字符;遇到 emoji 或某些古文字(如 U+1F9D0 ?、U+20BB7 ¥),它们属于增补字符(surrogate pairs),单个 char 拿不到完整语义。String.codePoints() 返回 IntStream,每个 int 就是完整的 Unicode 码点(0x0000 到 0x10FFFF),绕过了 char 的局限。
常见错误是误用 chars():它返回 IntStream 但按 UTF-16 编码单元拆分,对增补字符会拆成两个值(高代理 + 低代理),导致后续处理出错。比如 "??".chars().count() 返回 4,而 "??".codePoints().count() 才是 1。
遍历码点时别直接强转成 char
码点值可能大于 Character.MAX_VALUE(0xFFFF),直接 (char) cp 会截断高位,把 U+1F468(?)变成 U+F468(一个私有区乱码)。正确做法是用 Character.toChars(int codePoint) 转成 char[],再构造字符串:
String s = "??";
s.codePoints().forEach(cp -> {
String ch = new String(Character.toChars(cp));
System.out.println("码点: 0x" + Integer.toHexString(cp) + ", 字符: " + ch);
});
- 若只需判断或过滤,直接用
cp比较即可(如cp >= 0x1F600 && cp 匹配表情) - 若要拼接回字符串,必须用
Character.toChars(),不能String.valueOf((char) cp) - 注意组合字符序列(如带变音符号的字母)会被视为多个独立码点,
codePoints()不做归一化
和 String.split() / Pattern 不兼容,需手动切分
codePoints() 是流式 API,不提供索引或子串切分能力。想按码点位置截取(比如前 5 个 emoji),不能用 substring()(它按 char 计数),得先收集码点再重建:
List<integer> cps = str.codePoints().boxed().collect(Collectors.toList());
String firstFive = cps.subList(0, Math.min(5, cps.size()))
.stream()
.mapToObj(Character::toChars)
.map(String::new)
.collect(Collectors.joining());</integer>
性能敏感场景慎用:每次 toChars() 都新建数组,频繁调用建议缓存或改用 StringBuilder 逐个 append。
替代方案:用 Normalizer 处理组合字符更稳妥
codePoints() 拆的是 Unicode 码点,但像 "é" 可能是单个预组字符(U+00E9)或组合序列(U+0065 + U+0301)。如果业务逻辑依赖“视觉上一个字符”,仅靠 codePoints() 不够,得先归一化:
String normalized = Normalizer.normalize(str, Normalizer.Form.NFC); normalized.codePoints().forEach(...);
否则同一语义的字符串在不同输入源下码点数量不同,导致计数、截断逻辑不稳定。这点在处理用户昵称、国际化文本时特别容易被忽略。










