- 编码: 将
String(字符序列) 转换成byte[](字节序列) 的过程,你需要指定一种编码规则(如 UTF-8, GBK, ISO-8859-1)来决定每个字符应该用哪些字节表示。 - 解码: 将
byte[](字节序列) 转换成String(字符序列) 的过程,你必须使用与编码时完全相同的编码规则,否则就会出现乱码。
下面我将详细讲解这两种转换,并提供最佳实践和常见陷阱。

核心要点:始终指定字符编码
Java 中有 String.getBytes() 和 new String(byte[]) 两个方法,它们都有一个“陷阱”。
String -> byte[] (编码)
不推荐的做法(使用平台默认编码)
String str = "你好,Java"; // 使用 JVM 默认字符集进行编码,不推荐! // 在不同操作系统或环境下,默认编码可能不同(Windows 可能是 GBK,Linux/macOS 通常是 UTF-8) byte[] bytesDefault = str.getBytes();
问题: 如果你的代码在 A 机器上(默认 UTF-8)运行,将 String 编码成 byte[],然后将这个 byte[] 发送到或保存到 B 机器上(默认 GBK),B 机器在解码时就会因为编码和解码使用的规则不一致而出现乱码。
推荐的做法(显式指定编码)

你应该始终使用 String.getBytes(String charsetName) 方法,并明确指定一个标准、可靠的编码,首选 StandardCharsets.UTF_8。
import java.nio.charset.StandardCharsets;
String str = "你好,Java";
// 推荐做法 1: 使用 StandardCharsets (Java 7+)
// 这是最清晰、最不容易出错的方式
byte[] bytesUtf8 = str.getBytes(StandardCharsets.UTF_8);
// 推荐做法 2: 使用字符集名称字符串
// 效果与上面相同,但需要处理 UnsupportedEncodingException(在 Java 7+ 中,如果传入的编码是标准的,这个异常理论上不会抛出)
// try {
// byte[] bytesUtf8_2 = str.getBytes("UTF-8");
// } catch (UnsupportedEncodingException e) {
// // 通常不会发生
// }
byte[] -> String (解码)
不推荐的做法(使用平台默认编码)
byte[] bytes = {-28, -67, -96, -27, -91, -67, -24, -81, -107, 74, 97, 118, 97}; // "你好,Java" 在 UTF-8 下的字节
// 使用 JVM 默认字符集进行解码,不推荐!
String strDefault = new String(bytes);
// 如果你的默认编码不是 UTF-8,这里就会得到乱码,"浣犲ソJava"
推荐的做法(显式指定编码)
解码时,同样必须使用 new String(byte[], charset) 构造函数,并指定与编码时完全相同的编码。

import java.nio.charset.StandardCharsets;
// 假设 bytes 是上面用 UTF-8 编码得到的字节数组
byte[] bytes = {-28, -67, -96, -27, -91, -67, -24, -81, -107, 74, 97, 118, 97};
// 推荐做法 1: 使用 StandardCharsets
String strUtf8 = new String(bytes, StandardCharsets.UTF_8);
System.out.println(strUtf8); // 输出: 你好,Java
// 推荐做法 2: 使用字符集名称字符串
try {
String strUtf8_2 = new String(bytes, "UTF-8");
System.out.println(strUtf8_2); // 输出: 你好,Java
} catch (UnsupportedEncodingException e) {
// 通常不会发生
}
完整代码示例
下面是一个完整的、可运行的示例,展示了正确和错误的做法。
import java.nio.charset.StandardCharsets;
public class StringByteConversion {
public static void main(String[] args) {
// 1. String -> byte[] (编码)
String originalStr = "这是一个测试 String。";
// --- 推荐做法:显式指定 UTF-8 编码 ---
byte[] encodedBytes = originalStr.getBytes(StandardCharsets.UTF_8);
System.out.println("成功将 String 编码为 byte[] (使用 UTF-8)");
System.out.println("原始字符串: " + originalStr);
System.out.println("编码后的字节数组: " + java.util.Arrays.toString(encodedBytes));
System.out.println("----------------------------------------");
// --- 错误示范:使用默认编码 ---
// 在一个默认编码不是 UTF-8 的环境中,这可能会导致问题
byte[] encodedBytesDefault = originalStr.getBytes();
System.out.println("使用默认编码将 String 编码为 byte[]");
System.out.println("原始字符串: " + originalStr);
System.out.println("编码后的字节数组: " + java.util.Arrays.toString(encodedBytesDefault));
System.out.println("----------------------------------------");
// 2. byte[] -> String (解码)
// 我们使用上面正确编码得到的字节数组来演示
// --- 推荐做法:使用相同的 UTF-8 解码 ---
String decodedStr = new String(encodedBytes, StandardCharsets.UTF_8);
System.out.println("成功将 byte[] 解码为 String (使用 UTF-8)");
System.out.println("解码后的字符串: " + decodedStr);
System.out.println("解码是否成功? " + originalStr.equals(decodedStr)); // true
System.out.println("----------------------------------------");
// --- 错误示范:解码时使用了错误的编码 ---
// 假设我们错误地用 ISO-8859-1 (Latin-1) 来解码 UTF-8 的字节
String wrongDecodedStr = new String(encodedBytes, java.nio.charset.StandardCharsets.ISO_8859_1);
System.out.println("错误示范:用 ISO-8859-1 解码 UTF-8 字节");
System.out.println("解码后的字符串 (乱码): " + wrongDecodedStr);
System.out.println("解码是否成功? " + originalStr.equals(wrongDecodedStr)); // false
System.out.println("----------------------------------------");
}
}
常见字符编码
| 编码名称 | 描述 | 适用场景 |
|---|---|---|
| UTF-8 | 强烈推荐,Unicode 的可变长度编码,可以表示全球几乎所有字符,向后兼容 ASCII。 | Web 开发、文件存储、网络通信、数据库连接的首选标准。 |
| GBK / GB2312 | 中国国家标准编码,主要用于简体中文。 | 处理一些旧的、仅包含简体中文的文件或系统,尤其是在中国大陆。 |
| ISO-8859-1 (Latin-1) | 单字节编码,只能表示西欧语言字符,一个重要特性是:它不会造成乱码,任何字节序列都能被它解码,但非 ASCII 字符会被显示为 。 | 有时用作一种“安全”的中间编码,或者在不涉及多语言文本的场景下。 |
| Big5 | 繁体中文编码,主要在台湾、香港使用。 | 处理繁体中文相关的旧系统或文件。 |
- 永远不要依赖 JVM 的默认编码,它会使你的代码在不同环境下产生不可预测的行为。
- 编码和解码必须使用相同的字符集,这是避免乱码的唯一法则。
- 优先使用
StandardCharsets.UTF_8,对于现代应用,UTF-8 是事实上的标准,能最大程度地保证兼容性和正确性。 byte[]本身没有编码信息,它只是一堆字节,你必须通过外部约定(比如协议头、文件元数据)或者约定俗成的方式(默认就是 UTF-8)来知道它应该用哪种编码来解析。
