Java中何时用String,何时用StringBuilder

时间:2025-02-21 07:18:00
大家都知道,String对象是不可变的,不可变性会带来效率问题。为String对象重载“+”操作符时,都会自动生成一个新的String对象。
也有人说,String在上述的问题中已经会自动引入StringBuilder来解决效率问题。


为此,我在《Java编程思想》中找到了答案。


第一个小例子:

package ;

public class TestString {
	public static void main(String[] args)
	{
		String mango = "mango";
		String someting = "abc" + mango + "def" + 47;
		(someting);
	}
}
执行编译,命令:
javac
用javap来反编译上述代码,看看都发生了什么故事:
javap -c TestString


结果如下:

Compiled from ""
public class  extends {
public ();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
   4:	return

public static void main([]);
  Code:
   0:	ldc	#2; //String mango
   2:	astore_1
   3:	new	#3; //class java/lang/StringBuilder
   6:	dup
   7:	invokespecial	#4; //Method java/lang/StringBuilder."<init>":()V
   10:	ldc	#5; //String abc
   12:	invokevirtual	#6; //Method java/lang/:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:	aload_1
   16:	invokevirtual	#6; //Method java/lang/:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   19:	ldc	#7; //String def
   21:	invokevirtual	#6; //Method java/lang/:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   24:	bipush	47
   26:	invokevirtual	#8; //Method java/lang/:(I)Ljava/lang/StringBuilder;
   29:	invokevirtual	#9; //Method java/lang/:()Ljava/lang/String;
   32:	astore_2
   33:	getstatic	#10; //Field java/lang/:Ljava/io/PrintStream;
   36:	aload_2
   37:	invokevirtual	#11; //Method java/io/:(Ljava/lang/String;)V
   40:	return

}

从上述代码中看,编译器确实创建了一个StringBuilder对象。
但是,这并不能因此就说明可以随意的使用String对象。下面又举了个例子:

package ;

public class StringAndBuilder {
	public String implicit(String[] fields)
	{
		String result="";
		for(int i=0;i<;++i)
		{
			result+=fields[i];
		}
		return result;
	}
	
	public String explicit(String[] fields)
	{
		StringBuilder result=new StringBuilder();
		for(int i=0;i<;++i)
		{
			(fields[i]);
		}
		return ();
	}
}

反编译一下:
[linc@localhost TestString]$ javac  
[linc@localhost TestString]$ javap -c StringAndBuilder

Compiled from ""
public class  extends {
public ();
  Code:
   0:	aload_0
   1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
   4:	return

public  implicit([]);
  Code:
   0:	ldc	#2; //String 
   2:	astore_2
   3:	iconst_0
   4:	istore_3
   5:	iload_3
   6:	aload_1
   7:	arraylength
   8:	if_icmpge	38
   11:	new	#3; //class java/lang/StringBuilder
   14:	dup
   15:	invokespecial	#4; //Method java/lang/StringBuilder."<init>":()V
   18:	aload_2
   19:	invokevirtual	#5; //Method java/lang/:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22:	aload_1
   23:	iload_3
   24:	aaload
   25:	invokevirtual	#5; //Method java/lang/:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   28:	invokevirtual	#6; //Method java/lang/:()Ljava/lang/String;
   31:	astore_2
   32:	iinc	3, 1
   35:	goto	5
   38:	aload_2
   39:	areturn

public  explicit([]);
  Code:
   0:	new	#3; //class java/lang/StringBuilder
   3:	dup
   4:	invokespecial	#4; //Method java/lang/StringBuilder."<init>":()V
   7:	astore_2
   8:	iconst_0
   9:	istore_3
   10:	iload_3
   11:	aload_1
   12:	arraylength
   13:	if_icmpge	30
   16:	aload_2
   17:	aload_1
   18:	iload_3
   19:	aaload
   20:	invokevirtual	#5; //Method java/lang/:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   23:	pop
   24:	iinc	3, 1
   27:	goto	10
   30:	aload_2
   31:	invokevirtual	#6; //Method java/lang/:()Ljava/lang/String;
   34:	areturn

}

注意到implicit方法,从第8行到第35行构成了一个循环体。StringBuilder是在循环体内构造的,也就是说,每经过一次循环,就会创建一个新的StrinBuilder对象。
再看explicit方法,循环部分的代码更简短、简单,而且只生成了一个StrinBuilder对象。


结论:
当为一个类编写toString()方法时,如果操作比较简单,就可以信赖编译器,它会为你合理的构造最终的字符串结果。如果使用了循环,那么最好自己创建一个StringBuilder对象。
如果拿不准该用那种方式,就用javap来分析你的程序吧!


另外,linc之前还翻译了一段小文章,是讲String、StringBuffer和StringBuilder之间的区别的:/lincyang/article/details/6333041