Java中CharSequence转UTF-8 byte[]方法及编码长度异常问题
直接导致编译失败的错误
代码在Java 16环境下跑不起来和版本兼容无关,是本身存在3个基础语法/API调用错误:
- 导入语句写法错误:
java.nio.charset是包名不是类名,原代码写的import java.nio.charset;无法导入Charset类,正确写法是import java.nio.charset.Charset;或者批量导入包下所有类import java.nio.charset.*; - 异常未声明:
checkEquals方法中直接抛出受检异常Exception,但方法签名没有声明throws Exception,所有调用该方法的上层方法也没有做异常捕获或声明,会被编译器直接拦截。 - 调用不存在的方法:
main方法中调用了未定义的test()方法,实际应该直接调用编写的charSequenceToUtf8方法。
10个'A'返回11字节数组的原因
修完上述编译错误后出现的长度不符问题,是NIO Buffer的典型误用:Charset.encode()返回的ByteBuffer会预分配一块大于等于实际编码结果长度的内存作为后备数组,byteBuffer.length()返回的是当前有效数据的长度(即limit和position的差值),但调用byteBuffer.array()会返回整个后备数组——这个数组的长度是缓冲区的总容量(capacity),通常比有效数据长度大,未使用的位置默认填充0值。
测试2-9个'A'时刚好缓冲区预分配容量和有效数据长度相等,所以没暴露问题;当输入10个'A'时缓冲区预分配了11字节容量,最后1字节的默认0值被当成有效数据读出,才会得到长度11、末尾多0的结果。
核心误区:永远不要假设ByteBuffer的array()方法返回的数组长度等于有效编码结果长度。
额外说明:代码里写死断言编码后字节长度等于字符长度,这个逻辑本身就不成立——UTF-8是变长编码,ASCII字符确实占1字节,但中文、emoji等字符占2-4字节不等,这个断言只在纯ASCII输入下偶然成立。
根据需求选对应写法即可:
写法1:最简洁无坑(日常开发推荐)
不需要手动操作NIO缓冲区,Java 7+全版本兼容:
import java.nio.charset.StandardCharsets; public static byte[] charSequenceToUtf8(final CharSequence input) { if (input == null) { return new byte[0]; } return input.toString().getBytes(StandardCharsets.UTF_8); }
注意不要用重载的
getBytes(String charsetName)方法传"UTF-8"字符串,避免拼写错误抛出受检异常,直接用StandardCharsets里预定义的常量即可。
写法2:避免中间String对象的NIO实现
如果不想通过toString()生成中间String对象,手动操作NIO时一定要注意只截取有效数据段,不要直接返回整个后备数组:
import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; public static byte[] charSequenceToUtf8(final CharSequence input) { if (input == null) { return new byte[0]; } CharBuffer charBuffer = CharBuffer.wrap(input); ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer); // 仅复制缓冲区中实际存储有效数据的片段 byte[] result = Arrays.copyOfRange( byteBuffer.array(), byteBuffer.position(), byteBuffer.limit() ); // 清空缓冲区释放引用 byteBuffer.clear(); charBuffer.clear(); return result; }
内容的提问来源于stack exchange,提问作者Ian Boyd

