1. 简介

在 Java 开发中,我们经常需要在 Stringbyte[] 之间做转换。本文将详细讲解这两种操作的实现方式。

首先介绍如何将字符串转换为字节数组(编码),然后讲解反向操作(解码)。

2. 字符串转字节数组

在 Java 中,String 是以 Unicode 字符数组的形式存储的。要将其转换为字节数组,就需要把字符序列映射为字节序列。这个过程依赖于一个 Charset 实例,它定义了字符与字节之间的映射关系。

这个过程我们称之为 编码(Encoding)

Java 提供了多种方式将字符串编码为字节数组,下面逐一介绍。

2.1. 使用 String.getBytes()

String 类提供了三种重载的 getBytes() 方法:

使用平台默认字符集编码:

String inputString = "Hello World!";
byte[] byteArray = inputString.getBytes();

⚠️ 该方法依赖平台默认字符集,不推荐用于需要明确编码的场景。

使用指定名称的字符集编码:

@Test
public void whenGetBytesWithNamedCharset_thenOK() 
  throws UnsupportedEncodingException {
    String inputString = "Hello World!";
    String charsetName = "IBM01140";

    byte[] byteArray = inputString.getBytes("IBM01140");
    
    assertArrayEquals(
      new byte[] { -56, -123, -109, -109, -106, 64, -26,
        -106, -103, -109, -124, 90 },
      byteArray);
}

❌ 若字符集不支持,会抛出 UnsupportedEncodingException

使用 Charset 实例编码:

@Test
public void whenGetBytesWithCharset_thenOK() {
    String inputString = "Hello ਸੰਸਾਰ!";
    Charset charset = Charset.forName("ASCII");

    byte[] byteArray = inputString.getBytes(charset);

    assertArrayEquals(
      new byte[] { 72, 101, 108, 108, 111, 32, 63, 63, 63,
        63, 63, 33 },
      byteArray);
}

✅ 使用标准字符集编码:

@Test
public void whenGetBytesWithStandardCharset_thenOK() {
    String inputString = "Hello World!";
    Charset charset = StandardCharsets.UTF_16;

    byte[] byteArray = inputString.getBytes(charset);

    assertArrayEquals(
      new byte[] { -2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0,
        111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33 },
      byteArray);
}

2.2. 使用 Charset.encode()

Charset 类提供了一个便捷方法 encode(),可以将字符串编码为字节缓冲区:

@Test
public void whenEncodeWithCharset_thenOK() {
    String inputString = "Hello ਸੰਸਾਰ!";
    Charset charset = StandardCharsets.US_ASCII;

    byte[] byteArray = charset.encode(inputString).array();

    assertArrayEquals(
      new byte[] { 72, 101, 108, 108, 111, 32, 63, 63, 63, 63, 63, 33 },
      byteArray);
}

✅ 不支持的字符会被替换为默认字节(如 63 对应 ASCII 的 ?)。

2.3. 使用 CharsetEncoder

对于更精细的控制,可以使用 CharsetEncoder 类:

@Test
public void whenUsingCharsetEncoder_thenOK()
  throws CharacterCodingException {
    String inputString = "Hello ਸੰਸਾਰ!";
    CharsetEncoder encoder = StandardCharsets.US_ASCII.newEncoder();
    encoder.onMalformedInput(CodingErrorAction.IGNORE)
      .onUnmappableCharacter(CodingErrorAction.REPLACE)
      .replaceWith(new byte[] { 0 });

    byte[] byteArray = encoder.encode(CharBuffer.wrap(inputString))
                          .array();

    assertArrayEquals(
      new byte[] { 72, 101, 108, 108, 111, 32, 0, 0, 0, 0, 0, 33 },
      byteArray);
}

✅ 可以自定义对非法输入或不可映射字符的处理方式(忽略、替换或报告错误)。

3. 字节数组转字符串

将字节数组转换为字符串的过程称为 解码(Decoding),同样需要使用字符集。

⚠️ 解码时必须使用与编码时相同的字符集,否则可能导致乱码。

3.1. 使用 String 构造器

String 类提供了多个构造器支持从字节数组构造字符串:

使用平台默认字符集解码(不推荐):

@Test
public void whenStringConstructorWithDefaultCharset_thenOK() {
    byte[] byteArray = { 72, 101, 108, 108, 111, 32, 87, 111, 114,
      108, 100, 33 };
    
    String string = new String(byteArray);
    
    assertNotNull(string);
}

使用指定名称字符集解码:

@Test
public void whenStringConstructorWithNamedCharset_thenOK()
    throws UnsupportedEncodingException {
    String charsetName = "IBM01140";
    byte[] byteArray = { -56, -123, -109, -109, -106, 64, -26, -106,
      -103, -109, -124, 90 };

    String string = new String(byteArray, charsetName);
        
    assertEquals("Hello World!", string);
}

使用 Charset 实例解码:

@Test
public void whenStringConstructorWithCharSet_thenOK() {
    Charset charset = Charset.forName("UTF-8");
    byte[] byteArray = { 72, 101, 108, 108, 111, 32, 87, 111, 114,
      108, 100, 33 };

    String string = new String(byteArray, charset);

    assertEquals("Hello World!", string);
}

使用标准字符集解码:

@Test
public void whenStringConstructorWithStandardCharSet_thenOK() {
    Charset charset = StandardCharsets.UTF_16;
        
    byte[] byteArray = { -2, -1, 0, 72, 0, 101, 0, 108, 0, 108, 0,
      111, 0, 32, 0, 87, 0, 111, 0, 114, 0, 108, 0, 100, 0, 33 };

    String string = new String(byteArray, charset);

    assertEquals("Hello World!", string);
}

3.2. 使用 Charset.decode()

Charset 类也提供了 decode() 方法,可以直接从 ByteBuffer 解码为字符串:

@Test
public void whenDecodeWithCharset_thenOK() {
    byte[] byteArray = { 72, 101, 108, 108, 111, 32, -10, 111,
      114, 108, -63, 33 };
    Charset charset = StandardCharsets.US_ASCII;
    String string = charset.decode(ByteBuffer.wrap(byteArray))
                      .toString();

    assertEquals("Hello orl!", string);
}

✅ 不合法的字节会被替换为字符集的默认替换字符。

3.3. 使用 CharsetDecoder

如果需要更精细地控制解码过程,可以使用 CharsetDecoder

@Test
public void whenUsingCharsetDecoder_thenOK()
  throws CharacterCodingException {
    byte[] byteArray = { 72, 101, 108, 108, 111, 32, -10, 111, 114,
      108, -63, 33 };
    CharsetDecoder decoder = StandardCharsets.US_ASCII.newDecoder();

    decoder.onMalformedInput(CodingErrorAction.REPLACE)
      .onUnmappableCharacter(CodingErrorAction.REPLACE)
      .replaceWith("?");

    String string = decoder.decode(ByteBuffer.wrap(byteArray))
                      .toString();

    assertEquals("Hello ?orl?!", string);
}

✅ 可以自定义替换字符,也可以设置为 REPORT 来抛出异常。

4. 总结

本文介绍了 Java 中字符串与字节数组之间的编码与解码方法。选择合适的方式主要取决于以下因素:

  • 是否需要明确指定字符集
  • 是否需要处理非法输入
  • 是否需要更高的控制粒度

在实际开发中,推荐使用 StandardCharsets 中的标准字符集,避免依赖平台默认字符集带来的不确定性。

源码地址:GitHub - core-java-string-conversions-2


原始标题:Convert String to Byte Array and Reverse in Java