char不能可靠代表一个unicode字符,因其仅为utf-16编码单元,无法表示u+10000以上的码点;超出bmp的字符需用代理对(两个char)表示,单独操作会导致语义丢失或非法字符。

Java 中的 char 是 16 位无符号整数,对应 UTF-16 的一个编码单元,但它**不等于一个 Unicode 字符**。很多中文、emoji 和古文字的 Unicode 码点超出基本多语言平面(BMP),需要两个 char(即代理对)才能完整表示。直接用 char 处理,容易切开代理对、计数错误或显示方块。
为什么 char 不能可靠代表一个字符
Unicode 码点范围是 U+0000 到 U+10FFFF,共约 110 万个字符。而 char 只能表示 0x0000–0xFFFF(65536 个值)。U+10000 以上的字符(如 ? U+1F601、? U+20BB7)在 UTF-16 中必须拆成高位代理(high surrogate)和低位代理(low surrogate)两个 char 组合。单独操作其中任意一个,就失去语义,甚至变成非法字符。
正确处理 Unicode 字符的推荐方式
应以“码点(code point)”为单位,而非 char 索引:
- 用
String.codePointCount(int beginIndex, int endIndex)获取真实字符数(例如 "??" 返回 1,不是 2) - 用
String.codePointAt(int index)获取指定位置的完整码点(自动识别代理对) - 遍历时用
String.offsetByCodePoints(int index, int offset)跳转到下一个码点起始位置,避免停在代理对中间 - 构造字符串时,优先使用
Character.toString(int codePoint),而不是强制转char;它会自动返回含 1 或 2 个char的正确字符串
什么时候可以安全用 char
仅当明确限定输入范围为 BMP 内字符时(如纯 ASCII、常用汉字 U+4E00–U+9FFF、拉丁扩展-A/B),char 操作才基本安全。但即便如此,仍建议统一使用码点 API,避免未来混入 emoji 或生僻字导致隐性 Bug。
常见误用与修复示例
错误写法(截断代理对):String s = "?"; char c = s.charAt(0); // 得到 '\uD83D',不是笑脸
正确写法:
String s = "?"; int cp = s.codePointAt(0); // 得到 0x1F601<br> String face = Character.toString(cp); // 安全还原为 "?"
若需逐字符处理中文文本(如分词、校验),务必用 codePointAt() + offsetByCodePoints() 循环,而非 charAt(i++)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











