增补字符是unicode中码点大于u+ffff的字符,java中需用代理对(两个char)表示,遍历时应使用codepoints()或codepointat()配合charcount(),避免charat()和length()误判;数据库、前端、序列化等环节均需全链路utf-8支持。

增补字符(Supplementary Characters)是 Unicode 中码点大于 U+FFFF 的字符,比如部分生僻汉字、古汉字、扩展 B 区及以后的汉字、 emoji 表情(如 ?、??)、日文异体字等。它们在 Java 中无法用单个 char 表示,因为 char 是 16 位无符号整数,最大只能表示到 0xFFFF。这类字符必须用两个 char(即一个代理对,surrogate pair)来存储。
识别增补字符是否存在
不要依赖 String.length() 或 charAt(i) 判断字符数量或逐个取“字符”——它们按 char 单位计数/访问,会把一个增补字符当成两个“字符”处理。
- 用
str.codePointCount(0, str.length())获取真实 Unicode 字符个数 - 用
str.codePointAt(i)获取指定位置开始的代码点(返回 int),再配合i += Character.charCount(cp)跳过整个字符(可能是 1 或 2 个 char) - 用
Character.isSupplementaryCodePoint(int cp)判断是否为增补字符
安全地遍历和操作字符串
传统 for 循环 + charAt() 容易切开代理对,导致乱码或异常。应改用基于代码点的遍历方式:
- 使用
String.codePoints()(Java 8+)返回IntStream,天然按代码点遍历 - 或手动用
codePointAt()+charCount()移动索引(如知识库中示例所示) - 避免直接对增补字符字符串调用
substring()、split()(尤其含正则时)、replace()(未用codePoint意图时)等可能破坏代理对的操作
数据库与外部系统协作注意事项
增补字符在跨系统传输时极易丢失或错乱,需端到端确认支持:
- 数据库连接必须启用 UTF-8(如 MySQL 加
?characterEncoding=utf8mb4),SQL Server 需用SC排序规则(如Latin1_General_100_CI_AS_SC_UTF8)并确保版本 ≥ 2019 - SQL 函数如
LEN()、SUBSTRING()、LEFT()在旧排序规则下会把增补字符算作 2 个长度,且可能截断代理对;建议改用支持代码点的函数(如 SQL Server 2022+ 的STRING_AGG+STRING_SPLIT配合COLLATE Latin1_General_100_CI_AS_SC) - 前端页面需声明
<meta charset="UTF-8">,输入法需支持 Unicode 扩展区(如 Windows 新版微软拼音、macOS 原生输入法)
Java 11+ 的便利增强
新版 Java 提供了更直观的 API,减少出错可能:
-
String.isBlank()、strip()、stripLeading()等方法内部已按代码点逻辑实现,可安全用于含增补字符的字符串 -
String.transform()(Java 12+)和String.indent()(Java 12+)也默认尊重 Unicode 字符边界 - 序列化/JSON 库(如 Jackson、Gson)需确认配置为输出 UTF-8 字节而非转义,否则增补字符可能被写成
\ud83d\udc69这类代理对转义,而非原生字符










