stringbuilder.length()返回字符数而非字节数;按utf-8字节长度截取需先getbytes(utf_8),再截断字节数组并new string()解码,必要时回退至合法字符边界。

Java 中 StringBuilder 本身不直接处理“字节长度”,它操作的是 Unicode 字符(char,即 UTF-16 编码单元),不是字节。中文等多字节字符在 UTF-8 编码下占多个字节,但 StringBuilder.length() 返回的是字符数(code unit 数),不是字节数。要按**字节长度截取**(例如适配数据库字段限制、HTTP 请求体大小、日志截断等场景),必须显式编码为字节数组再处理。
明确:length() 是字符数,不是字节数
StringBuilder.length() 返回的是 UTF-16 code unit 的数量。一个常见误区是认为它等于 UTF-8 字节数——实际完全不等:
- ASCII 字符(如
'a'):1 个字符 = 1 个 UTF-8 字节 - 常用中文(如
'中'):1 个字符 = 3 个 UTF-8 字节(UTF-8 编码) - 部分生僻字或 Emoji(如 ?):可能占用 2 个
char(代理对),但仍是 1 个 Unicode 码点,UTF-8 下占 4 字节
按 UTF-8 字节长度安全截取字符串
不能直接用 substring() 或 delete() 靠字符索引,因为字符索引 ≠ 字节位置。正确做法是:
- 将完整字符串转为 UTF-8 字节数组:
str.getBytes(StandardCharsets.UTF_8) - 按需截取字节数组前 N 位
- 再用
new String(byte[], StandardCharsets.UTF_8)解码回字符串(JDK 7+ 自动处理截断导致的非法 UTF-8 序列)
⚠️ 注意:直接截断字节数组可能导致末尾是半个 UTF-8 编码(如只取了中文前 2 字节),但 JDK 的 String(byte[], charset) 构造器会自动替换非法序列为 (U+FFFD),不会抛异常。如需严格避免乱码,应从后往前检查并退回到合法字符边界(见下一条)。
更健壮:截断到最近的完整 Unicode 字符边界
若业务要求“绝不出现 ”,需确保截断点落在合法 UTF-8 字符结尾。可借助 java.nio.charset.CharsetDecoder 或手动校验 UTF-8 字节模式,但更实用的方式是:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 先获取原始字符串,用
String.getBytes(StandardCharsets.UTF_8)得到字节数组 - 从字节长度上限位置向前扫描,跳过 UTF-8 续字节(0x80–0xBF),找到起始字节(0xC0–0xF7 或 0x00–0x7F)
- 用该位置截断,再解码
简单实现示例(不依赖外部库):
public static String truncateByUtf8Bytes(String str, int maxBytes) {
if (str == null || maxBytes 0 && (bytes[end] & 0xC0) == 0x80) end--; // 跳过续字节
// 检查是否为起始字节(支持 1~4 字节 UTF-8)
if (end >= 0) {
int b = bytes[end] & 0xFF;
if (b >= 0xFC) end -= 5; // 不可能(UTF-8 最多 4 字节),此处仅示意逻辑
else if (b >= 0xF8) end -= 4;
else if (b >= 0xF0) end -= 3;
else if (b >= 0xE0) end -= 2;
else if (b >= 0xC0) end -= 1;
// ASCII 或已对齐,end 即为截断位置
}
if (end
实际项目推荐使用 Apache Commons Text 的 StringUtils.abbreviate()(按字符)或自定义工具类封装上述逻辑,避免重复出错。
StringBuilder 场景下的典型用法建议
如果你正在用 StringBuilder 拼接内容,并最终需要按字节截断:
- 拼接完成后调用
toString(),再用上述方法截断 —— 不要在StringBuilder内部按字节操作(它无此 API) - 避免反复
toString().getBytes(),尤其在循环中;可缓存字节数组或预估最大字符数(如:纯中文按 3 字节/字估算,留余量) - 若目标是「不超过 N 字节」且允许少量冗余,可用字符数粗略上限:最多截取
Math.min(sb.length(), (int) Math.ceil(maxBytes / 3.0))个字符,再验证字节长度并微调
本质上,StringBuilder 是字符层面的高效拼接工具,字节层面的约束必须交由 String + Charset 协同完成。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










