字符串性能瓶颈在于隐式创建、重复分配与不当复用;应避免隐式装箱、循环内字符串拼接及冗余编码转换,优先使用 stringbuilder、指定编码的构造方法和缓存字节数组,并审慎使用 intern()。

字符串转换本身不慢,真正拖慢性能的是隐式创建、重复分配和不当复用。关键不是“转不转”,而是“怎么转”和“转几次”。
避免隐式装箱与中间字符串对象
像 String.valueOf(obj) 或 "" + obj 这类写法,在循环中反复调用会触发大量临时 String 实例生成。尤其当 obj 是数字或自定义对象时,toString() 调用 + 字符串拼接会叠加开销。
- 对数字:直接用
Integer.toString(i)比String.valueOf(i)略快(无类型检查开销) - 对固定格式拼接:优先用
StringBuilder.append(i),而不是先转成字符串再拼 - 禁止在循环内写
str += i—— 它等价于每次新建 StringBuilder,再 toString()
谨慎使用 String 构造函数转换
用 new String(byte[]) 或 new String(char[]) 创建字符串,会强制复制底层数组,绕过常量池且多一次内存分配。
- 如果源数据来自网络或文件读取的 byte[],且内容确定为 UTF-8,用
new String(bytes, StandardCharsets.UTF_8)更安全,也避免平台默认编码风险 - 若 byte[]/char[] 内容不变,且后续只读,可考虑复用该数组构造字符串,但不要频繁 new
- 字面量拼接如
"prefix" + value + "suffix"在编译期会被优化为单个常量;但含变量时仍走运行时拼接逻辑
减少编码转换次数
UTF-8 ↔ UTF-16 的来回转换(比如多次调用 string.getBytes(StandardCharsets.UTF_8) 后又 new String())是隐形性能杀手,尤其在日志、HTTP 请求体、JSON 序列化等场景高频出现。
- 保持编码上下文一致:IO 层用 UTF-8 处理,内存中统一用 String(UTF-16),避免反复编解码
- 若需多次获取字节数组,缓存
bytes字段(注意线程安全),而非每次都调用 getBytes() - 用
StandardCharsets.UTF_8替代字符串名"UTF-8",避免 Charset.forName() 的查找开销
按需选择 intern(),别当成万能去重
intern() 能减少重复字符串内存占用,但代价是同步方法调用 + 常量池哈希查找。滥用会导致 GC 压力上升甚至死锁风险(尤其 JDK 7 以前)。
- 适合长期存活、高复用率的字符串,如配置项 key、状态码、协议字段名
- 绝对不要在循环里对动态生成字符串(如
"user_" + id)调用 intern() - JDK 7+ 后常量池移到堆内存,intern() 开销降低,但仍建议配合弱引用缓存做更可控的复用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











