1. 简介
在 Java 开发中,我们经常需要在 String
和 byte[]
之间做转换。本文将详细讲解这两种操作的实现方式。
首先介绍如何将字符串转换为字节数组(编码),然后讲解反向操作(解码)。
2. 字符串转字节数组
在 Java 中,String
是以 Unicode 字符数组的形式存储的。要将其转换为字节数组,就需要把字符序列映射为字节序列。这个过程依赖于一个 Charset
实例,它定义了字符与字节之间的映射关系。
这个过程我们称之为 编码(Encoding)。
Java 提供了多种方式将字符串编码为字节数组,下面逐一介绍。
2.1. 使用 String.getBytes()
String
类提供了三种重载的 getBytes()
方法:
getBytes()
:使用平台默认字符集编码getBytes(String charsetName)
:使用指定名称的字符集编码getBytes(Charset charset)
:使用指定的Charset
实例编码
✅ 使用平台默认字符集编码:
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
中的标准字符集,避免依赖平台默认字符集带来的不确定性。