直接用 substring() 按字符数截取会破坏 utf-16 代理对或 utf-8 多字节边界,导致乱码;应转 utf-8 字节数组后按字节安全截取,并校验起始位置,或使用 apache commons lang 的 stringutils.abbreviate() 自动处理边界与省略号。

直接用 substring() 按字符数截取,遇到中文时容易在 UTF-16 代理对或 UTF-8 多字节边界处“砍断”,造成乱码或 。关键不是少截几个字符,而是按**字节边界安全截取**,同时确保全程编码明确。
按指定字节数截取(推荐 UTF-8 场景)
适用于日志摘要、文件名截短、API 返回限制等需控制实际传输长度的场景。核心是:先转 UTF-8 字节数组 → 截取字节 → 再转回字符串,并处理可能的截断点。
- 不要用
str.substring(0, n),它按 Unicode 码点(char 数)截,不考虑字节结构 - 用
str.getBytes(StandardCharsets.UTF_8)获取真实 UTF-8 字节数组 - 从末尾向前检查,跳过 UTF-8 多字节字符的中间字节(即避开 0x80–0xBF 区间)
- 常见做法:截到不超过目标字节数的最大合法 UTF-8 起始位置,再构造字符串
使用 Apache Commons Lang 的 StringUtils.abbreviate()
省去手动处理字节边界的复杂逻辑,内部已做 UTF-8 安全判断,适合快速开发。
- 引入依赖:
org.apache.commons:commons-lang3 - 示例:
StringUtils.abbreviate("你好世界abc", 8)→ 返回"你好世..."(不会截出半个“界”) - 它会自动识别字符边界,保留完整字符并添加省略号,比裸写更鲁棒
Web 或 I/O 场景:源头统一编码 + 显式指定
很多“截取后乱码”其实源于读取阶段就已损坏——比如用平台默认编码读文件,再截取只是放大问题。
- 文件读取务必指定编码:
new InputStreamReader(new FileInputStream(f), StandardCharsets.UTF_8) - HTTP 请求/响应头显式声明:
Content-Type: text/plain; charset=utf-8 - JVM 启动参数加
-Dfile.encoding=UTF-8,避免依赖系统默认(Windows 是 GBK) - Maven 项目配置
<project.build.sourceencoding>UTF-8</project.build.sourceencoding>
数据库与前端联动时的隐性截断
例如 MySQL VARCHAR(50) 存的是 UTF-8 字节长度,而 Java 的 length() 返回的是字符数。50 个中文占 150 字节,但字段只允许 50 字节 → 实际存不下。
- 建表时用
utf8mb4并确认字段长度按字节设计(如VARCHAR(150)才能存 50 个中文) - 插入前校验字节长度:
str.getBytes(StandardCharsets.UTF_8).length - 前端输入限制也应基于字节(JS 中
new TextEncoder().encode(str).length),而非str.length
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











