String 与 StringBuilder 的比较

在 Java 中,StringStringBuilder 都是用于处理字符串的类,但它们在不可变性性能、和适用场景等方面存在显著区别。

1. 不可变性

  • String:不可变对象(immutable),一旦创建了字符串对象,内容就不能再改变。每次对 String 执行修改操作(如拼接、替换等)时,都会生成一个新的 String 对象。
  • StringBuilder:可变对象(mutable),允许对字符串内容进行修改,能够在原对象的基础上进行拼接、删除等操作。

2. 性能

  • String:由于每次修改字符串都会创建新对象,所以在多次拼接或修改字符串时会产生大量临时对象,导致性能下降,尤其在循环中频繁修改字符串时,效率低下。
  • StringBuilderStringBuilder 允许在原对象基础上修改字符串,避免了创建多个临时对象,内存和速度效率都更高,特别适合在循环中对字符串进行频繁修改的场景。

3. 线程安全性

  • String:是不可变的,因此是线程安全的,可以在多个线程中安全地使用。
  • StringBuilder:是可变的,但不保证线程安全。如果在多线程环境中需要安全地修改字符串内容,应使用 StringBuffer(另一种线程安全的可变字符串类)代替 StringBuilder

4. 使用场景

  • String:适合不需要修改的字符串内容,比如静态文本、常量字符串、少量拼接操作。
  • StringBuilder:适合需要频繁拼接、修改字符串内容的场景,比如构建动态 SQL 语句、大量文本处理、或在循环中拼接字符串等。

5. 示例代码

  • String 拼接示例(会创建多个临时对象,性能较差):

    1
    2
    3
    4
    5
    String result = "Hello";
    for (int i = 0; i < 5; i++) {
    result += " World"; // 每次都会创建新的 String 对象
    }
    System.out.println(result); // 输出:Hello World World World World World
  • StringBuilder 拼接示例(在原对象基础上修改,性能更高):

    1
    2
    3
    4
    5
    StringBuilder sb = new StringBuilder("Hello");
    for (int i = 0; i < 5; i++) {
    sb.append(" World"); // 修改原对象,不创建新对象
    }
    System.out.println(sb.toString()); // 输出:Hello World World World World World

总结

特性 String StringBuilder
是否可变 不可变 可变
性能 修改操作较慢 修改操作较快
线程安全性 线程安全 非线程安全
适用场景 少量或不可变字符串操作 频繁拼接、修改字符串的操作

结论:当需要对字符串进行大量修改操作时,使用 StringBuilder 会显著提高性能。对于静态文本或少量操作,使用 String 更为合适。

字符串练习题解析

第一题解析

代码:

1
2
3
4
5
6
7
8
9
public class Test {
public static void main(String[] args) {
String s1 = "java";
String s2 = "java";
String s3 = new String("java");
String s4 = new StringBuilder("java").toString();
System.out.printf("%b,%b,%b", s1 == s2, s3 == s4, s1.equals(s4));
}
}

选项解析

  • s1 == s2:结果为 true,因为 s1s2 都指向字符串常量池中的 "java" 字符串,指向相同的内存地址。
  • s3 == s4:结果为 false,因为 s3 是通过 new String("java") 创建的对象,s4 是通过 new StringBuilder("java").toString() 创建的对象,二者是不同的实例,指向不同的内存地址。
  • s1.equals(s4):结果为 true,因为 equals 方法比较的是字符串内容,s1s4 的内容都是 "java"

正确答案B. true,false,true


第二题解析

代码:

1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
String s1 = "hello";
System.out.println(s1.substring(1, 3));
}
}

选项解析

  • s1.substring(1, 3)substring 方法提取字符串 s1 从索引 1 到索引 3(不包括 3)的子字符串。
    • 索引 1 对应字符 'e',索引 2 对应字符 'l',因此 s1.substring(1, 3) 返回 "el"

正确答案C. el


第三题解析

代码:

1
2
3
4
5
6
public class Test {
public static void main(String[] args) {
String s1 = "java";
s1 += "123";
}
}

选项解析

  • 这道题问的是,s1 所引用的字符串对象内容是否会被修改为 "java123"
  • 答案为“错误”,因为 String 是不可变对象(immutable)。执行 s1 += "123"; 实际上相当于 s1 = s1 + "123";,会创建一个新的字符串对象 "java123",并将 s1 重新指向这个新对象,而原来的 "java" 字符串对象并没有被修改。
  • 因此,s1 引用的字符串对象内容并未被修改,而是指向了一个新的对象。

正确答案错误