// 看似正确,实则在补充字符面前失效
public String reverseBad(char[] value) {
for (int i = 0, j = value.length - 1; i < j; i++, j--) {
char tmp = value[i];
value[i] = value[j];
value[j] = tmp;
}
return new String(value);
}
测试 "😀😃😄"(Emoji 为 UTF-16 代理对,每个占 2 个 char):
输入: [D83D, DE00, D83D, DE03, D83D, DE04] // 3 个 Emoji 输出: [DE04, D83D, DE03, D83D, DE00, D83D] // 乱码:High/Low Surrogate 被拆分
JDK 源码 (AbstractStringBuilder.reverse()) 核心逻辑:
char 数组整体反转Character.MIN_SURROGATE (\\uD800) ~ Character.MAX_SURROGATE (\\uDFFF)Low Surrogate 后跟 High Surrogate 则交换二者,恢复正确代理对顺序public AbstractStringBuilder reverse() {
boolean hasSurrogate = false;
int n = count - 1;
// 第一步:整体反转 char 数组
for (int j = (n - 1) >> 1; j >= 0; --j) {
char c1 = value[j];
char c2 = value[n - j];
if (!hasSurrogate) {
hasSurrogate = (c1 >= Character.MIN_SURROGATE && c1 <= Character.MAX_SURROGATE)
|| (c2 >= Character.MIN_SURROGATE && c2 <= Character.MAX_SURROGATE);
}
value[j] = c2;
value[n - j] = c1;
}
// 第二步:修复代理对顺序
if (hasSurrogate) {
for (int i = 0; i < count - 1; i++) {
char low = value[i];
if (Character.isLowSurrogate(low)) {
char high = value[i + 1];
if (Character.isHighSurrogate(high)) {
value[i++] = high;
value[i] = low;
}
}
}
}
return this;
}
| 术语 | 定义 | Java API |
|---|---|---|
| Code Point (代码点) | 一个完整的 Unicode 字符,范围 U+0000 ~ U+10FFFF | Character.toCodePoint(high, low), codePointAt() |
| Code Unit (代码单元) | UTF-16 编码单元,Java char 即 16-bit 代码单元 | char 类型 |
| BMP (基本多文种平面) | U+0000 ~ U+FFFF,单 char 即可表示 | Character.isBmpCodePoint() |
| Supplementary Characters (增补字符) | U+10000 ~ U+10FFFF,需 2 个 char (代理对) | Character.isSupplementaryCodePoint() |
| High Surrogate (高位代理) | \\uD800 ~ \\uDBFF,代理对首位 | Character.isHighSurrogate(char) |
| Low Surrogate (低位代理) | \\uDC00 ~ \\uDFFF,代理对次位 | Character.isLowSurrogate(char) |
// 1. 统计真实字符数(代码点数)
public int realLength(String s) {
return s.codePointCount(0, s.length()); // 或 s.chars().count()
}
// 2. 按代码点遍历
public void forEachCodePoint(String s, IntConsumer action) {
s.codePoints().forEach(action); // Java 8+ Stream API
}
// 3. 安全反转(保持代理对完整)
public String safeReverse(String s) {
int[] codePoints = s.codePoints().toArray();
for (int i = 0, j = codePoints.length - 1; i < j; i++, j--) {
int tmp = codePoints[i];
codePoints[i] = codePoints[j];
codePoints[j] = tmp;
}
return new String(codePoints, 0, codePoints.length);
}
// 4. 按代码点截取子串
public String substringByCodePoint(String s, int beginCp, int endCp) {
int begin = s.offsetByCodePoints(0, beginCp);
int end = s.offsetByCodePoints(0, endCp);
return s.substring(begin, end);
}
String.length() 返回 char 数,非字符数:"😀".length() == 2charAt(index) 可能返回半个代理对:"😀".charAt(0) == '\\uD83D'(无意义). 默认匹配单个 char:用 \\X 或 (?s). 匹配整个代码点String.getBytes(StandardCharsets.UTF_8) 正确编码为 UTF-8 字节流,反序列化用 new String(bytes, StandardCharsets.UTF_8)codePoints(), codePointCount(), offsetByCodePoints())char[]/String)utf8mb4 (MySQL) 或 UTF8 (PostgreSQL) 支持增补字符