在 Java 中,获取字符串(String)的编码并不是一个直接的操作,因为 String 类本身不存储编码信息,这一点是理解 Java 字符串处理的核心。

下面我将详细解释这个问题,并提供几种在不同场景下获取或判断字符串编码的方法。
核心概念:Java String 内部是 UTF-16
你必须理解 Java 的 String 内部是如何工作的:
- Unicode 字符集:Java 使用 Unicode 字符集来表示字符,这意味着每个字符都有一个唯一的数字码点(Code Point),'A' 的码点是
U+0041,'中' 的码点是U+4E2D。 - UTF-16 编码:Java 的
String类内部使用 UTF-16 这种编码格式来存储这些 Unicode 字符,它将每个字符表示为一个或两个char值(16位)。- 大多数常见的字符(如 ASCII 字符和大部分 BMP 平面内的字符)可以用一个
char表示。 - 一些不常见的字符(如某些表情符号或特殊符号)需要用一对
char值(称为“代理对”,surrogate pair)来表示。
- 大多数常见的字符(如 ASCII 字符和大部分 BMP 平面内的字符)可以用一个
当你创建一个 String 对象时,它的编码已经是 UTF-16 了,你不需要、也不能从一个 String 对象中“获取”它的编码,因为它就是 UTF-16。
常见的误解与实际需求
为什么大家会问“如何获取字符串的编码”呢?这个问题背后隐藏着以下两种实际需求:

- 我有一个字节数组,我想把它解码成
String,但我不知道它原来的编码是什么? - 我有一个
String,我想把它转换成字节数组,并指定一种编码(如 UTF-8 或 GBK)来保存或传输。
下面我们针对这两种需求提供解决方案。
从字节数组创建 String(如何处理未知编码)
这是最常见的问题,你收到一段字节数据(比如从文件读取、从网络接收),但不知道它是由哪种编码(GBK, UTF-8, ISO-8859-1 等)生成的。
问题示例
byte[] bytes = "你好,世界".getBytes("GBK"); // 假设这串字节是用 GBK 编码的
// 如果我用错误的编码去解析它
String wrongString = new String(bytes, "UTF-8"); // 错误!
System.out.println(wrongString); // 输出会是乱码,æ¨å¥½ï¼ä¸ç
解决方案
由于 Java String 内部是 UTF-16,创建 String 的过程必然是一个解码过程,你必须告诉 Java 如何将字节数组解释成字符,如果编码猜错了,就会得到乱码。
方法 1:指定编码(最推荐)

如果你能确定或猜测出编码,请务必在创建 String 时指定它。
byte[] bytes = "你好,世界".getBytes("GBK");
// 正确:使用 GBK 解码
String correctString = new String(bytes, "GBK");
System.out.println(correctString); // 输出: 你好,世界
方法 2:使用 Charset 类(更现代、更安全)
java.nio.charset.Charset 类是处理字符集的首选方式,它比直接使用字符串名称(如 "GBK")更安全,可以避免因编码名称拼写错误导致的异常。
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
byte[] bytes = "你好,世界".getBytes("GBK");
// 推荐:使用 Charset 对象
Charset gbkCharset = Charset.forName("GBK");
String correctString = new String(bytes, gbkCharset);
System.out.println(correctString); // 输出: 你好,世界
// 如果编码不存在,会抛出 UnsupportedCharsetException
try {
Charset unknownCharset = Charset.forName("UNKNOWN-ENCODING");
} catch (UnsupportedCharsetException e) {
System.err.println("不支持的编码: " + e.getMessage());
}
方法 3:尝试多种编码(不完美,但有时是唯一办法)
如果编码完全未知,你可以尝试一个常见的编码列表,直到找到一个能正常显示的字符串为止,这是一种启发式方法,并不总是可靠。
byte[] bytes = "你好,世界".getBytes("GBK");
String[] charsetsToTry = {"UTF-8", "GBK", "GB2312", "ISO-8859-1", "Big5"};
String result = null;
boolean found = false;
for (String charsetName : charsetsToTry) {
try {
String temp = new String(bytes, charsetName);
// 这里可以加入更复杂的逻辑来判断是否是乱码,例如检查是否有大量 '?'
// 简单起见,我们假设能成功解码就是对的
result = temp;
found = true;
System.out.println("成功使用编码: " + charsetName);
break;
} catch (Exception e) {
// 忽略异常,继续尝试
}
}
if (found) {
System.out.println("最终结果: " + result);
} else {
System.out.println("无法确定编码,所有尝试均失败。");
}
将 String 转换为字节数组(如何指定编码)
当你需要将一个 String 保存到文件或通过网络发送时,你需要将它转换成字节数组,这时,你必须明确指定一种编码。
问题示例
String str = "你好,世界";
// 默认情况下,使用 JVM 的默认字符集(可能是 UTF-8,也可能是其他)
byte[] defaultBytes = str.getBytes(); // 不推荐!
System.out.println(new String(defaultBytes, StandardCharsets.UTF_8)); // 在 UTF-8 环境下是正确的
// 明确指定使用 UTF-8 编码
byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);
System.out.println(new String(utf8Bytes, StandardCharsets.UTF_8)); // 正确
// 明确指定使用 GBK 编码
byte[] gbkBytes = str.getBytes("GBK");
System.out.println(new String(gbkBytes, "GBK")); // 正确
最佳实践
始终明确指定编码,不要依赖 JVM 的默认字符集,因为不同环境下的默认字符集可能不同,导致程序行为不一致。
String str = "Hello, 世界";
// 推荐做法 1: 使用 StandardCharsets (适用于 UTF-8, ISO-8859-1, US-ASCII)
byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);
// 推荐做法 2: 使用 Charset.forName (适用于所有编码)
Charset gbkCharset = Charset.forName("GBK");
byte[] gbkBytes = str.getBytes(gbkCharset);
| 问题 | 核心概念 | 解决方案 |
|---|---|---|
如何获取 String 的编码? |
String 内部永远是 UTF-16,没有“获取”一说。 |
N/A |
我有一个字节数组,如何转成 String? |
这是一个解码过程,必须指定原始编码。 | new String(byteArray, "charsetName") 或 new String(byteArray, Charset.forName("...")) |
我有一个 String,如何转成字节数组? |
这是一个编码过程,必须指定目标编码。 | str.getBytes("charsetName") 或 str.getBytes(StandardCharsets.UTF_8) |
| 字节数组的编码未知怎么办? | 只能猜测或尝试,没有 100% 准确的方法。 | 优先尝试:UTF-8, GBK, ISO-8859-1。 文件头分析:有些文件格式(如 XML)会在开头声明编码。 启发式分析:通过统计字符分布等复杂算法来判断(非常复杂)。 |
记住这个黄金法则:
byte[](字节流) 有编码,String(字符流) 没有。
byte[]->String是解码,必须指定编码。String->byte[]是编码,必须指定编码。
