杰瑞科技汇

Java字符串与byte数组如何互转?

核心概念

  1. 字符串 (String):

    • Java 使用 char 类型来表示一个字符,在标准的 Java 虚拟机中,一个 char 占用 2 个字节,并且采用的是 UTF-16 编码。
    • String 对象是不可变的,一旦创建,其内容就不能被修改。
    • String 是一个高级的、面向 Unicode 字符的抽象。
  2. 字节数组 (byte[]):

    • byte 是 Java 的基本数据类型,占用 1 个字节
    • 字节数组是底层的、面向字节的序列,它本身不包含任何关于字符集或编码的信息。
    • 它常用于存储二进制数据(如图片、文件内容)或需要以字节形式传输/存储的文本数据。

关键点: 字符串和字节数组之间的转换需要一个“桥梁”,这个桥梁就是 字符编码(Character Encoding),编码定义了如何将字符(char)序列转换为字节(byte)序列,以及如何反向转换。

最常见的编码是 UTF-8,它是 Unicode 的一种可变长度编码方式,能高效地表示全世界几乎所有语言的字符,并且与 ASCII 兼容。


字符串 转换为 字节数组 (String -> byte[])

要将一个字符串转换为字节数组,我们需要使用 String 类的 getBytes() 方法。

方法 1: 使用平台的默认字符集 (不推荐)

String str = "Hello, 世界!";
// 使用 JVM 默认的字符集进行编码
byte[] byteArray = str.getBytes();
// 注意:这种方式依赖于运行环境,可能导致不一致的行为
// 在 Windows 中文系统上可能是GBK,在 Linux 上可能是UTF-8

为什么不推荐? 因为代码的可移植性差,在不同的操作系统或环境中,JVM 的默认字符集可能不同,导致同一个字符串在不同机器上转换后的字节数组也不同,从而引发难以排查的问题。

方法 2: 指定字符集 (推荐)

这是最安全、最推荐的方式,显式地告诉 Java 使用哪种编码(如 UTF-8)。

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
String str = "Hello, 世界!";
// 推荐方式 1 (Java 7+): 使用 StandardCharsets 枚举 (最安全)
byte[] byteArrayUtf8 = str.getBytes(StandardCharsets.UTF_8);
// 推荐方式 2: 使用字符串指定字符集
byte[] byteArrayGbk = null;
try {
    byteArrayGbk = str.getBytes("GBK"); // GBK 是中文常用的编码
} catch (UnsupportedEncodingException e) {
    // JVM 不支持指定的编码,会抛出此异常
    e.printStackTrace();
}
// 打印结果
System.out.println("UTF-8 字节数组: " + java.util.Arrays.toString(byteArrayUtf8));
System.out.println("GBK 字节数组: " + java.util.Arrays.toString(byteArrayGbk));

输出示例:

UTF-8 字节数组: [72, 101, 108, 108, 111, 44, 32, -28, -72, -83, -27, -107, -116, 33]
GBK 字节数组: [72, 101, 108, 108, 111, 44, 32, -42, -48, -50, -60, -61, 33]

可以看到,同一个中文字符“世界”,在 UTF-8 和 GBK 编码下,对应的字节是完全不同的。


字节数组 转换为 字符串 (byte[] -> String)

将字节数组转换回字符串,需要使用 String 的构造函数,并且同样需要指定正确的字符集。

方法 1: 使用平台的默认字符集 (不推荐)

byte[] byteArray = ...; // 某个字节数组
// 使用 JVM 默认的字符集进行解码
String str = new String(byteArray);
// 同样,这种方式依赖于运行环境,非常危险!

方法 2: 指定字符集 (推荐)

这是必须的,因为解码时使用的编码必须和当初编码时使用的编码完全一致,否则会出现乱码。

import java.nio.charset.StandardCharsets;
// 假设我们有一个 UTF-8 编码的字节数组
byte[] originalByteArray = "Hello, 世界!".getBytes(StandardCharsets.UTF_8);
// 使用相同的字符集进行解码
String recoveredStr = new String(originalByteArray, StandardCharsets.UTF_8);
System.out.println("恢复的字符串: " + recoveredStr); // 输出: Hello, 世界!
// --- 错误示范 ---
// 如果用错误的字符集解码,就会出现乱码
String garbledStr = new String(originalByteArray, "ISO-8859-1"); // ISO-8859-1 不支持中文
System.out.println("乱码的字符串: " + garbledStr); // 输出可能类似: Hello, ä¸ç界!

乱码的原理:

  • 编码 "世界" (UTF-8): [-28, -72, -83, -27, -107, -116]
  • 用 ISO-8859-1 解码:它会把每个字节当作一个独立的拉丁字符,所以会尝试查找 -28 对应的字符,-72 对应的字符... 最终得到一堆无法识别的符号。

完整示例与最佳实践

下面是一个综合示例,展示了编码和解码的完整过程。

import java.nio.charset.StandardCharsets;
public class StringAndBytesExample {
    public static void main(String[] args) {
        // 1. 定义一个包含多种字符的原始字符串
        String originalStr = "这是一个测试: Test 123, 你好! 😊";
        System.out.println("--- 1. 字符串 -> 字节数组 ---");
        // 2. 使用推荐的 UTF-8 编码将字符串转换为字节数组
        byte[] utf8Bytes = originalStr.getBytes(StandardCharsets.UTF_8);
        System.out.println("原始字符串: " + originalStr);
        System.out.println("UTF-8 字节数组长度: " + utf8Bytes.length);
        System.out.println("UTF-8 字节数组内容: " + java.util.Arrays.toString(utf8Bytes));
        System.out.println("\n--- 2. 字节数组 -> 字符串 ---");
        // 3. 使用相同的 UTF-8 编码将字节数组解码回字符串
        String decodedStr = new String(utf8Bytes, StandardCharsets.UTF_8);
        System.out.println("解码后的字符串: " + decodedStr);
        // 4. 验证数据是否一致
        System.out.println("解码后是否与原始字符串一致? " + originalStr.equals(decodedStr));
        System.out.println("\n--- 3. 错误示范:使用不同编码 ---");
        // 5. 假设我们有一个 GBK 编码的字节数组(但不知道)
        byte[] gbkBytes = originalStr.getBytes(java.nio.charset.Charset.forName("GBK"));
        System.out.println("GBK 字节数组长度: " + gbkBytes.length);
        // 6. 错误地用 UTF-8 去解码 GBK 字节数组
        String wrongDecodedStr = new String(gbkBytes, StandardCharsets.UTF_8);
        System.out.println("用 UTF-8 解码 GBK 字节数组的结果(乱码): " + wrongDecodedStr);
        // 7. 正确地用 GBK 去解码 GBK 字节数组
        String correctDecodedStr = new String(gbkBytes, java.nio.charset.Charset.forName("GBK"));
        System.out.println("用 GBK 解码 GBK 字节数组的结果(正确): " + correctDecodedStr);
    }
}

总结与最佳实践

操作 推荐方法 原因
String -> byte[] str.getBytes(StandardCharsets.UTF_8) 可移植性:不受 JVM 默认字符集影响,在任何环境下行为一致。
明确性:代码清晰,意图明确。
安全性:避免了因环境不同导致的不可预测问题。
byte[] -> String new String(byteArray, StandardCharsets.UTF_8) 必须指定:解码时必须知道原始的编码。
一致性:解码编码必须与编码时使用的编码完全一致,否则必然乱码。
选择字符集 UTF-8 通用性:能表示全球所有字符。
兼容性:与 ASCII 兼容,对于纯英文文本,每个字符只占 1 字节。
行业标准:现代 Web、数据库、API 的首选编码。

核心原则编码和解码必须使用相同的字符集。 这是处理字符串和字节数组转换时最重要的规则,在不确定的情况下,始终默认使用 UTF-8

分享:
扫描分享到社交APP
上一篇
下一篇