核心概念:字符编码
计算机中存储的都是二进制数据(byte 数组)。String 在 Java 中是使用 Unicode 字符序列来表示的。byte 数组要转换成 String,就必须知道这些二进制数据是用哪种字符编码(如 UTF-8, ISO-8859-1, GBK 等)编码成的。

byte[]->String: 需要一个解码过程,即按照指定的编码规则,将二进制数据解析成字符。String->byte[]: 需要一个编码过程,即按照指定的编码规则,将字符序列转换成二进制数据。
将 byte[] 转换为 String
这是最常见的转换场景,不指定编码或使用错误的编码是导致乱码( 或乱码符号)的主要原因。
推荐方法:显式指定编码
这是最安全、最可靠的方法,你应该始终明确地告诉 Java 使用哪种编码。
import java.nio.charset.StandardCharsets;
public class BytesToString {
public static void main(String[] args) {
// 一个包含中文字符的 byte 数组,这些字节是用 UTF-8 编码的
// '你' 的 UTF-8 编码是三个字节: (byte)0xE4, (byte)0xBD, (byte)0xA0
// '好' 的 UTF-8 编码是三个字节: (byte)0xE5, (byte)0xA5, (byte)0xBD
byte[] utf8Bytes = {(byte) 0xE4, (byte) 0xBD, (byte) 0xA0, (byte) 0xE5, (byte) 0xA5, (byte) 0xBD};
// --- 正确的转换方式 ---
// 使用 UTF-8 编码进行解码
String strFromUtf8 = new String(utf8Bytes, StandardCharsets.UTF_8);
System.out.println("使用 UTF-8 解码: " + strFromUtf8); // 输出: 你好
// --- 错误的转换方式 ---
// 如果用错误的编码(如 ISO-8859-1)去解码,会得到乱码
String strFromIso = new String(utf8Bytes, "ISO-8859-1");
System.out.println("使用 ISO-8859-1 解码 (错误): " + strFromIso); // 输出: ä½ å¥½
}
}
代码解释:
new String(byte[] bytes, Charset charset): 这是推荐的构造方法。StandardCharsets: Java 7 引入的工具类,提供了常用编码的Charset对象,如UTF_8,ISO_8859_1,US_ASCII等,使用它可以避免拼写错误,比直接传入字符串"UTF-8"更安全。new String(byte[] bytes, String charsetName): 这种方法也可以,但charsetName是一个字符串,如果拼写错误(如"UFT-8"),在运行时才会抛出UnsupportedCharsetException,编译器无法检查。
将 String 转换为 byte[]
这个过程同样需要指定编码。

推荐方法:显式指定编码
import java.nio.charset.StandardCharsets;
public class StringToBytes {
public static void main(String[] args) {
String originalString = "你好,世界!";
// --- 正确的转换方式 ---
// 使用 UTF-8 编码进行编码
byte[] utf8Bytes = originalString.getBytes(StandardCharsets.UTF_8);
System.out.println("使用 UTF-8 编码后的字节长度: " + utf8Bytes.length); // 输出: 18 (因为中文字符在UTF-8中占3个字节)
// --- 错误的转换方式 ---
// 如果用错误的编码(如 ISO-8859-1)去编码,非ASCII字符(如中文)会被替换成 '?'
byte[] isoBytes = originalString.getBytes(StandardCharsets.ISO_8859_1);
System.out.println("使用 ISO-8859-1 编码后的字节长度: " + isoBytes.length); // 输出: 12
System.out.println("使用 ISO-8859-1 编码后的内容: " + new String(isoBytes, StandardCharsets.ISO_8859_1)); // 输出: ??????
}
}
代码解释:
String.getBytes(Charset charset): 这是推荐的编码方法。String.getBytes(): 这是一个重载方法,如果不带任何参数,它会使用 JVM 的默认字符集。这通常是不推荐的,因为不同操作系统或 JVM 配置的默认字符集可能不同(可能是 UTF-8,也可能是 GBK),这会导致你的程序在不同环境下产生不一致的结果,是常见的“坑”。
完整示例与常见问题
下面是一个完整的例子,演示了正确的“编码-解码”循环以及错误的操作。
import java.nio.charset.StandardCharsets;
public class ByteStringConversionExample {
public static void main(String[] args) {
String originalText = "Hello, Java! 你好,世界!";
// 1. 正确的转换流程:使用 UTF-8
System.out.println("--- 正确使用 UTF-8 ---");
// String -> byte[] (编码)
byte[] utf8Bytes = originalText.getBytes(StandardCharsets.UTF_8);
System.out.println("原始字符串: " + originalText);
System.out.println("编码后的 byte[] (UTF-8): " + java.util.Arrays.toString(utf8Bytes));
System.out.println("编码后的长度: " + utf8Bytes.length);
// byte[] -> String (解码)
String decodedText = new String(utf8Bytes, StandardCharsets.UTF_8);
System.out.println("解码后的字符串: " + decodedText);
System.out.println("解码后与原始字符串是否相等: " + originalText.equals(decodedText)); // true
System.out.println();
// 2. 错误的转换流程:使用不匹配的编码
System.out.println("--- 错误:编码和解码使用不同编码 ---");
// String -> byte[] (用 ISO-8859-1 编码)
byte[] isoBytes = originalText.getBytes(StandardCharsets.ISO_8859_1);
System.out.println("用 ISO-8859-1 编码后的长度: " + isoBytes.length);
// byte[] -> String (尝试用 UTF-8 解码)
String wrongDecodedText = new String(isoBytes, StandardCharsets.UTF_8);
System.out.println("用 UTF-8 解码 ISO-8859-1 字节的结果: " + wrongDecodedText); // 会是乱码
System.out.println();
// 3. 特殊情况:使用 ISO-8859-1 的“无损”特性
System.out.println--- ISO-8859-1 的特殊应用 ---");
// ISO-8859-1 (Latin-1) 的特点是它对 0-255 的每个字节都有唯一的字符映射,不会丢失信息。
// 先用一种编码(如 UTF-8)得到字节,再用 ISO-8859-1 解码,可以“安全”地得到一个中间表示。
// 再用 ISO-8859-1 编码,最后用正确的编码(如 UTF-8)解码,可以恢复原始字符串。
// 步骤 1: 正确编码 (e.g., UTF-8)
byte[] correctUtf8Bytes = originalText.getBytes(StandardCharsets.UTF_8);
// 步骤 2: 用 ISO-8859-1 解码 (这不会产生乱码,但得到的字符串是错误的字符)
// (byte) 0xE4 会被解码成 'ä'
String intermediateString = new String(correctUtf8Bytes, StandardCharsets.ISO_8859_1);
System.out.println("用 ISO-8859-1 解码 UTF-8 字节得到的中间字符串: " + intermediateString);
// 步骤 3: 用 ISO-8859-1 编码这个中间字符串
// 这一步会“恢复”出原始的 byte[],因为 ISO-8859-1 是单字节映射
byte[] recoveredBytes = intermediateString.getBytes(StandardCharsets.ISO_8859_1);
// 步骤 4: 用正确的编码 (UTF-8) 解码恢复的 byte[]
String finalRecoveredText = new String(recoveredBytes, StandardCharsets.UTF_8);
System.out.println("最终恢复的字符串: " + finalRecoveredText);
System.out.println("最终恢复的字符串是否与原始字符串相等: " + originalText.equals(finalRecoveredText)); // true
}
}
总结与最佳实践
- 永远不要依赖默认编码:避免使用
new String(byte[])和String.getBytes()不带参数的重载方法。 - 始终显式指定编码:在转换时,明确使用
StandardCharsets.UTF_8或其他你确定的编码。 - 保持编码一致:在数据的整个生命周期中(从生成、传输到存储),确保使用同一种编码。
- 首选 UTF-8:在没有任何特殊要求的情况下,UTF-8 是现代应用的最佳选择,它支持全球所有字符,且向后兼容 ASCII。
- 理解 ISO-8859-1 的特殊用途:当需要将
byte[]安全地作为“无损”的二进制数据在字符串中传递时,可以先用 UTF-8 等编码得到字节,然后用 ISO-8859-1 解码成字符串,之后再通过 ISO-8859-1 编码恢复原始字节,这在处理网络协议或文件格式时偶尔会用到。

