Java 里 String 用得到处都是,但一旦频繁拼接它就露怯,这时候该换 StringBuilder。这篇讲清楚两者的常用方法和怎么选。

String 是不可变的

String s = "hello";
s.toUpperCase();        // 返回新串,s 本身没变
System.out.println(s);  // 输出:hello

s = s.toUpperCase();    // 要用变量接住返回值
System.out.println(s);  // 输出:HELLO

String 的每个"修改"方法都不改原串,而是返回一个新串。忘了接返回值,后面代码就像什么都没发生过——这是新手最常见的坑。

常用方法速查

String s = "Java 入门";
s.length()               // 7,字符数
s.charAt(0)              // 'J'
s.contains("入门")       // true
s.indexOf("a")           // 1,第一次出现的位置
s.substring(5)           // "入门",从下标 5 截到结尾
s.replace("Java", "Go")  // "Go 入门"
s.trim()                 // 去掉两端空白
s.split(" ")             // ["Java", "入门"]

判断两个字符串相等,千万别用 ==:

String a = new String("hi");
String b = new String("hi");
System.out.println(a == b);       // false,比的是内存地址
System.out.println(a.equals(b));  // true,比的是内容

== 判断的是不是同一个对象,字符串比内容一律用 equals()。

拼接频繁时换 StringBuilder

先看反面教材:

String result = "";
for (int i = 1; i <= 1000; i++) {
    result += i;   // 每轮循环都创建一个新 String,又慢又费内存
}

+= 每一轮都造一个新对象,循环上千次性能明显下降。StringBuilder 在内部字符数组上直接改,攒完一次性转回 String:

StringBuilder sb = new StringBuilder();
for (int i = 1; i <= 5; i++) {
    sb.append(i).append(",");
}
System.out.println(sb.toString());   // 输出:1,2,3,4,5,

常用方法就三个:append() 追加、insert() 在指定位置插入、toString() 转回 String。还有个顺手的 reverse():

System.out.println(new StringBuilder("abc").reverse());  // 输出:cba

到底怎么选

  • 固定几段拼一次:直接用 +,简单直观,编译器会自己优化
  • 循环里反复拼接:果断换 StringBuilder
  • 多线程环境共享的可变字符串(非常少见):才轮到 StringBuffer 出场

一句话总结:写起来舒服用 +,跑起来要快用 StringBuilder。

小结

  • String 不可变,所有"修改"方法都返回新串,记得接住返回值
  • 比较内容用 equals(),== 只比地址
  • 循环拼接用 append(),最后 toString()
  • 偶尔拼一次用 + 没问题,不必过度优化