在Java中,String常量进行"+"运算时,编译器会进行常量折叠优化(如"Hello" + "World"直接合并为常量池中的"HelloWorld");而涉及变量时(如str1 + str2),编译器会隐式转换为StringBuilder的append操作,每次拼接均创建新String对象,导致频繁内存分配和性能开销,因此在循环或高频拼接场景中必须使用StringBuilder而非+运算符以避免效率问题。

当Java中String类型的变量和常量使用"+"运算符进行拼接时,实际上发生了以下过程:

1. 编译期优化(常量拼接)

如果拼接的是编译期可以确定的常量,JVM会进行常量折叠(Constant Folding)优化:

  • 例如:String str = "Hello, " + "World!"; 会被编译器在编译阶段直接优化为 String str = "Hello, World!";
  • 这种情况下,字符串拼接不会产生额外的临时对象,而是直接使用常量池中的字符串

2. 运行时处理(涉及变量)

如果拼接中包含变量,Java编译器会将其转换为StringBuilder操作:

  • 例如:String result = str1 + str2; 会被编译为:

    String result = new StringBuilder().append(str1).append(str2).toString();
    
  • 这个过程的内部机制:

    1. 隐式创建StringBuilder对象
    2. 调用append()方法添加字符串
    3. 最后调用toString()生成新字符串

3. 重要特点

  • 字符串不可变性:Java中的String是不可变对象,每次拼接都会创建新的String对象
  • 性能问题:在循环中使用+=操作符会导致严重性能问题,因为每次循环都会:
    • 创建新的StringBuilder对象
    • 复制现有内容
    • 添加新内容
    • 调用toString()生成新字符串
    • 产生大量临时对象和内存拷贝

4. 实际示例

String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";  // 编译期优化,常量拼接
String str4 = str1 + str2;     // 运行时转换为StringBuilder
String str5 = "string";

// 结果分析
System.out.println(str3 == str4); // false(str3来自常量池,str4是新对象)
System.out.println(str3 == str5); // true(都是常量池中的"string")
System.out.println(str4 == str5); // false(str4是新创建的对象)

5. 优化建议

  • 避免在循环中使用+=:应改用StringBuilder或StringBuffer
  • 使用预分配容量:StringBuilder sb = new StringBuilder(estimatedSize);
  • 对于单线程环境:优先使用StringBuilder(比StringBuffer更快)
  • 对于多线程环境:使用StringBuffer(线程安全)
  • 对于带分隔符的拼接:使用StringJoiner(Java 8引入)

6. 为什么需要这些优化?

在10万次字符串拼接测试中:

  • 使用+运算符:平均耗时420ms
  • 使用StringBuilder:平均耗时12ms(性能提升35倍)

这充分说明了在需要频繁拼接字符串的场景中,正确使用StringBuilder可以带来显著的性能提升。