ASCII 是 Unicode 的一个子集。 这意味着任何一个 ASCII 字符(代码点在 0-127 之间)本身也是一个 Unicode 字符,你不需要“转换” ASCII 字符 成为 Unicode,因为它 已经是 Unicode 了。

当人们说“ASCII 转 Unicode”时,他们真正想做的是以下两种事情之一:
- 获取字符串的 Unicode 代码点(Code Point)表示。
- 将字符串以 Unicode 编码(如 UTF-16 或 UTF-8)的形式存储或传输,即转换成字节数组。
下面我将针对这两种常见需求,提供详细的 Java 代码示例和解释。
获取字符串的 Unicode 代码点(Code Point)
这种方法用于获取字符串中每个字符的 Unicode 整数值,这对于字符分析、调试或理解字符串的内部表示非常有用。
示例 1:遍历字符并打印其 Unicode 值
String.charAt() 方法返回一个 char 类型,它可以表示大部分 Unicode 字符(基本多文种平面 BMP),但对于需要两个 char 表示的增补字符(Supplementary Characters)会返回一个“代理对”(Surrogate Pair),更可靠的方法是使用 String.codePointAt()。

public class AsciiToUnicodeExample {
public static void main(String[] args) {
// 一个包含标准ASCII字符和几个非ASCII(Unicode)字符的字符串
String str = "Hello 世界! 😊";
System.out.println("原始字符串: " + str);
System.out.println("----------------------------------");
// 方法1: 使用 codePointAt() 和 codePointCount()
// 这是处理所有 Unicode 字符(包括增补字符)最可靠的方式
System.out.println("--- 使用 codePointAt() 遍历 ---");
int length = str.codePointCount(0, str.length());
for (int i = 0; i < length; i++) {
int codePoint = str.codePointAt(i);
// 将代码点转换为char,用于打印字符本身
// 注意:如果代码点是增补字符,这个转换会得到代理对
char[] chars = Character.toChars(codePoint);
System.out.println(
"字符: " + new String(chars) +
", Unicode 代码点 (十进制): " + codePoint +
", Unicode 代码点 (十六进制): U+" + Integer.toHexString(codePoint).toUpperCase()
);
// 如果是增补字符,i 需要递增 2,否则递增 1
i += Character.charCount(codePoint) - 1;
}
System.out.println("\n----------------------------------");
// 方法2: 使用 charAt() (简单但不完全适用于所有Unicode字符)
System.out.println("--- 使用 charAt() 遍历 (可能不完整) ---");
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
// char 的范围是 0-65535 (0xFFFF),无法直接表示所有代码点
System.out.println(
"字符: " + c +
", Unicode 值 (十进制): " + (int) c +
", Unicode 值 (十六进制): U+" + Integer.toHexString(c).toUpperCase()
);
}
}
}
输出结果:
原始字符串: Hello 世界! 😊
----------------------------------
--- 使用 codePointAt() 遍历 ---
字符: H, Unicode 代码点 (十进制): 72, Unicode 代码点 (十六进制): U+48
字符: e, Unicode 代码点 (十进制): 101, Unicode 代码点 (十六进制): U+65
字符: l, Unicode 代码点 (十进制): 108, Unicode 代码点 (十六进制): U+6C
字符: l, Unicode 代码点 (十进制): 108, Unicode 代码点 (十六进制): U+6C
字符: o, Unicode 代码点 (十进制): 111, Unicode 代码点 (十六进制): U+6F
字符: 世, Unicode 代码点 (十进制): 19990, Unicode 代码点 (十六进制): U+4E16
字符: 界, Unicode 代码点 (十进制): 30028, Unicode 代码点 (十六进制): U+754C
字符: !, Unicode 代码点 (十进制): 33, Unicode 代码点 (十六进制): U+21
字符: 😊, Unicode 代码点 (十进制): 128578, Unicode 代码点 (十六进制): U+1F60A
----------------------------------
--- 使用 charAt() 遍历 (可能不完整) ---
字符: H, Unicode 值 (十进制): 72, Unicode 值 (十六进制): U+48
字符: e, Unicode 值 (十进制): 101, Unicode 值 (十六进制): U+65
字符: l, Unicode 值 (十进制): 108, Unicode 值 (十六进制): U+6C
字符: l, Unicode 值 (十进制): 108, Unicode 值 (十六进制): U+6C
字符: o, Unicode 值 (十进制): 111, Unicode 值 (十进制): 6F
字符: 世, Unicode 值 (十进制): 19990, Unicode 值 (十六进制): U+4E16
字符: 界, Unicode 值 (十进制): 30028, Unicode 值 (十六进制): U+754C
字符: !, Unicode 值 (十进制): 33, Unicode 值 (十六进制): U+21
字符: , Unicode 值 (十进制): 55357, Unicode 值 (十六进制): U+D83D
字符: , Unicode 值 (十进制): 56842, Unicode 值 (十六进制): U+DE0A
注意看 的区别:
codePointAt()正确地识别出这是一个代码点U+1F60A。charAt()将其拆分为两个“代理字符”(Surrogate Characters)U+D83D和U+DE0A,它们本身没有独立的语义,只是组合起来表示完整的 emoji。
将字符串编码为 Unicode 字节数组
这种方法通常用于将字符串序列化,以便存储在文件中、通过网络发送或与其他系统交互,Java 内部使用 UTF-16 编码,但你可以指定其他 Unicode 编码,如 UTF-8。
示例 2:使用 String.getBytes() 方法
String.getBytes(StandardCharsets charset) 是最推荐的方式,因为它明确指定了字符集,避免了平台默认编码带来的问题。

import java.nio.charset.StandardCharsets;
public class AsciiToBytesExample {
public static void main(String[] args) {
String str = "Hello 世界!";
System.out.println("原始字符串: " + str);
System.out.println("字符串长度: " + str.length()); // 9
// 1. 转换为 UTF-16 编码的字节数组 (Java内部默认编码)
// 每个ASCII字符占2字节,中文字符也占2或4字节
byte[] utf16Bytes = str.getBytes(StandardCharsets.UTF_16);
System.out.println("\nUTF-16 编码的字节数组:");
for (byte b : utf16Bytes) {
System.out.printf("%02X ", b);
}
System.out.println("\nUTF-16 字节数组长度: " + utf16Bytes.length); // 通常是 2 * 字符数 + 2 (BOM)
// 2. 转换为 UTF-8 编码的字节数组 (最通用、最节省空间的Unicode编码)
// ASCII字符占1字节,中文字符通常占3字节
byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);
System.out.println("\nUTF-8 编码的字节数组:");
for (byte b : utf8Bytes) {
System.out.printf("%02X ", b);
}
System.out.println("\nUTF-8 字节数组长度: " + utf8Bytes.length); // 5*1 + 2*3 + 1 = 12
// 3. 转换为平台默认编码的字节数组 (不推荐,因为结果在不同机器上可能不同)
// byte[] defaultBytes = str.getBytes(); // 弃用方式,不推荐使用
}
}
输出结果(取决于你的系统字节序,可能会有 BOM 标志的差异):
原始字符串: Hello 世界!
字符串长度: 9
UTF-16 编码的字节数组:
FE FF 00 48 00 65 00 6C 00 6C 00 6F 00 20 4E 16 75 4C 00 21
UTF-16 字节数组长度: 20
UTF-8 编码的字节数组:
48 65 6C 6C 6F 20 E4 B8 96 E7 95 8C 21
UTF-8 字节数组长度: 13
FE FF是 UTF-16 的字节顺序标记(BOM),表示大端序。48是 'H' 的 ASCII/UTF-8 值。E4 B8 96是 "世" 字的 UTF-8 编码,由3个字节组成。
总结与最佳实践
| 需求 | 推荐方法 | 说明 |
|---|---|---|
| 获取字符的 Unicode 代码点 | str.codePointAt(index) |
最准确,能正确处理所有 Unicode 字符,包括代理对。 |
| 将字符串转为字节数组(通用) | str.getBytes(StandardCharsets.UTF_8) |
强烈推荐,UTF-8 是互联网上最通用的编码,兼容 ASCII,且对非英文文本更节省空间。 |
| 将字符串转为字节数组(Java内部) | str.getBytes(StandardCharsets.UTF_16) |
如果你需要与 Java 内部表示或特定系统交互,注意它通常会产生比 UTF-8 更大的字节数组。 |
| 避免的错误做法 | str.getBytes() |
不指定字符集,使用平台默认编码,会导致程序在不同环境下行为不一致,是常见的 Bug 来源。 |
核心要点:
- ASCII 是 Unicode 的子集,ASCII 字符天然就是 Unicode 字符。
- “转换”通常指两种操作:获取代码点或编码为字节。
- 处理字符时,优先使用
codePointAt()和codePointCount()来确保完整性。 - 处理字节时,始终明确指定字符集,首选
StandardCharsets.UTF_8。
