IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [原]Java中何时用String,何时用StringBuilder

    lincyang发表于 2011-07-30 19:04:42
    love 0
    大家都知道,String对象是不可变的,不可变性会带来效率问题。为String对象重载“+”操作符时,都会自动生成一个新的String对象。
    也有人说,String在上述的问题中已经会自动引入StringBuilder来解决效率问题。


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


    第一个小例子:

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


    结果如下:

    Compiled from "TestString.java"
    public class com.linc.TestString.TestString extends java.lang.Object{
    public com.linc.TestString.TestString();
      Code:
       0:	aload_0
       1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
       4:	return
    
    public static void main(java.lang.String[]);
      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/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       15:	aload_1
       16:	invokevirtual	#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       19:	ldc	#7; //String def
       21:	invokevirtual	#6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       24:	bipush	47
       26:	invokevirtual	#8; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
       29:	invokevirtual	#9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       32:	astore_2
       33:	getstatic	#10; //Field java/lang/System.out:Ljava/io/PrintStream;
       36:	aload_2
       37:	invokevirtual	#11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
       40:	return
    
    }

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

    package com.linc.TestString;
    
    public class StringAndBuilder {
    	public String implicit(String[] fields)
    	{
    		String result="";
    		for(int i=0;i<fields.length;++i)
    		{
    			result+=fields[i];
    		}
    		return result;
    	}
    	
    	public String explicit(String[] fields)
    	{
    		StringBuilder result=new StringBuilder();
    		for(int i=0;i<fields.length;++i)
    		{
    			result.append(fields[i]);
    		}
    		return result.toString();
    	}
    }

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

    Compiled from "StringAndBuilder.java"
    public class com.linc.TestString.StringAndBuilder extends java.lang.Object{
    public com.linc.TestString.StringAndBuilder();
      Code:
       0:	aload_0
       1:	invokespecial	#1; //Method java/lang/Object."<init>":()V
       4:	return
    
    public java.lang.String implicit(java.lang.String[]);
      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/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       22:	aload_1
       23:	iload_3
       24:	aaload
       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_2
       32:	iinc	3, 1
       35:	goto	5
       38:	aload_2
       39:	areturn
    
    public java.lang.String explicit(java.lang.String[]);
      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/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
       23:	pop
       24:	iinc	3, 1
       27:	goto	10
       30:	aload_2
       31:	invokevirtual	#6; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
       34:	areturn
    
    }

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


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


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



沪ICP备19023445号-2号
友情链接