在 Java 中字符串是一个常用的东西,而其一个常用的操作是字符串的拼接,Java 对此提供了一种非常直观的操作方式——即 + 操作符。

String str0 = "a";
String str1 = str0 + "b";

如上的程序片段就实现了一个字符串的拼接,可以看到整个描述非常简洁,一目了然。学习 Java 的时候,这是接触到的第一种拼接字符串的方式,先入为主的印象会让你在以后需要拼接字符串时第一时间想到它,但是,你会听到大家都说:拼接大量的字符串时使用 StringBuilder 更好,这是为什么呢?

让我们看看编译器是怎么处理 + 操作符的。下面是上一个程序片段编译后的字节码指令:

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: new           #3                  // class java/lang/StringBuilder
       6: dup
       7: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      10: aload_1
      11: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      14: ldc           #6                  // String b
      16: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      19: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      22: astore_2
      23: return
}

通过阅读这段字节码,可以发现,我们的 + 拼接操作实际上被编译器理解成了这个样子:

String str0 = "a";
StringBuilder sb = new StringBuilder();
sb.append(str0).append("b");
String str1 = sb.toString();

中间多出来了一个 StringBuilder 对象,这是一个临时对象。

假设我们现在有这样一段程序片段:

String str0 = "a";
for (int i = 0; i < 10000; i++) {    
      str0 += "a";
}

再来看看编译后的字节码:

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: sipush        10000
       9: if_icmpge     38
      12: new           #3                  // class java/lang/StringBuilder
      15: dup
      16: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
      19: aload_1
      20: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: ldc           #2                  // String a
      25: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      28: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      31: astore_1
      32: iinc          2, 1
      35: goto          5
      38: return
}

5~35 是我们的循环体,在循环体里面,每次拼接都会生成一个 StringBuilder 的临时对象,那么这个程序片段执行下去就会产生 10000 个 StringBuilder 的临时对象。

这 10000 个临时对象都是必要的吗?

显然不是,我们可以在循环体外直接创建一个 StringBuilder 对象,然后在循环体中通过 append 方法拼接字符串,这样就省下了创建并回收 10000 个临时对象的消耗。

所以,在需要拼接大量字符串时,还是使用 StringBuilder 对象为好。

分类: CODE

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注