java中string不可变,频繁修改会加重gc压力;应复用字符串常量池对象、循环拼接用stringbuilder、安全使用substring、避免隐式低效转换。

Java 中的 String 是不可变类,每次修改(如拼接、截取、转换)都会生成新对象。频繁创建临时 String 对象会加重 GC 压力,尤其在循环或高频调用场景下极易引发内存问题。避免的关键不是“不用 String”,而是减少无意义的新建、复用已有实例、绕过中间对象。
优先使用字符串字面量,而非 new String()
字面量(如 "hello")自动进入字符串常量池,JVM 保证同一内容只存一份;而 new String("hello") 每次都新建堆对象,完全冗余。
- ✅ 推荐:
String s = "abc";—— 复用常量池对象 - ❌ 避免:
String s = new String("abc");—— 白白多一个堆对象 - ⚠️ 注意:即使内容相同,
new String("abc") == "abc"为false,因为引用不同
循环内拼接字符串必须用 StringBuilder
用 + 拼接字符串在循环中等于反复创建 String 对象:每次 a + b 都生成新 String,旧对象变垃圾。JVM 不会自动优化这种动态拼接。
- ✅ 推荐:在循环外初始化
StringBuilder sb = new StringBuilder();,循环内调用sb.append(...) - ✅ 若长度可预估,构造时指定容量:
new StringBuilder(1024),避免内部数组多次扩容 - ❌ 避免:
String result = ""; for (...) { result += item; }—— 每轮都新建 String
截取子串优先复用底层 char[](Java 7+ 更安全)
早期 Java 中 substring() 共享原字符串的 char 数组,虽节省内存但可能导致大字符串长期无法回收;Java 7u6 后已改为复制新数组,更安全但略增开销。不过只要不滥用,它仍是比手动 new String(char[], offset, len) 更简洁、更可控的方式。
- ✅ 可放心用:
String sub = original.substring(10, 20);—— 语义清晰,无需额外 new - ❌ 不推荐:
String sub = new String(original.toCharArray(), 10, 10);—— 多一次 char[] 拷贝和 String 构造 - ? 小技巧:若只是做判断(如
str.startsWith("prefix")),根本无需创建子串
注意包装类与字符串转换的隐式开销
像 Integer.toString(i)、String.valueOf(obj) 这些方法本身已做优化(缓存常用数字、复用逻辑),但若在循环中对同一值反复调用,仍建议提前计算并复用结果。
- ✅ 推荐:
final String idStr = String.valueOf(id); for (...) { log(idStr + "-item"); } - ❌ 避免:
for (...) { log(String.valueOf(id) + "-item"); }—— 每轮都调用 valueOf - ⚠️ 特别注意:
"" + i本质是编译器转成new StringBuilder().append("").append(i).toString(),等价于低效拼接,应避免
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











