String ,StringBuilder,StringBuffer的区别

String类代表字符串,java程序中所有的字符串字面值(如”abc”)都作为此类来实现,字符串是常量,它们的值在创建后就不能改变。因为字符串不可变,因此可以共享它们。


StringBuffer,线程安全的可变字符序列。一个类似于String的字符缓冲区,但不能修改,是指这个对象本身不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。


StringBuilder, 一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。
有时候,我们经常会这样写程序,譬如

public class Concatenation {
    public static void main(String[] args) {
            String mango = "mango";
            String s = "abc" + mango + "def" + 47;
            System.out.println(s);
    }
}

 然后我们运行程序,控制台便马上打印出了我们想要的结果。可是,在这个程序片段的内部,它的执行机理以及这样写,性能上是个什么样的情况,我们可能很少去研究,下面我将来谈一谈重载的’+’操作符与StringBuilder的区别。

首先,我们先说String。众所周知,String是java.lang包中的一个类,它在java编程中使用的非常频繁。查看javaAPI可以看到,这个类的声明是public final class String….,正因为是final的,可以想到这个类已经创建,那么它就是不可变的。有了这个认识,我们再说上面的程序,每用’+’操作符对String对象进行一次运算,那么它都会产生一个新的String对象,这个新产生的String对象既包含原来的String对象也包含添加进来的String对象。毫无疑问,上面那段代码必定能很好的工作,只是为了把几个字符串拼接到一起,按照上面的做法会产生大量的”中间”String对象,这些中间对象也是需要被GC回收的。为了看清楚这段代码的执行细节,我们可以用这么一个命令 
Javap  –c  Concatenation
控制台给我们列出了这样的信息:

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

public static void main(java.lang.String[]);
  Code:
   0:   ldc     #16; //String mango
   2:   astore_1
   3:   new     #18; //class java/lang/StringBuilder
   6:   dup
   7:   ldc     #20; //String abc
   9:   invokespecial  #22; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   12:  aload_1
   13:invokevirtual #25;//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   16:  ldc     #29; //String def
   18:  invokevirtual   #25; //Method java/lang/StringBuilder.append:(Ljava/lang
/String;)Ljava/lang/StringBuilder;
   21:  bipush  47
   23:  invokevirtual   #31; //Method java/lang/StringBuilder.append:(I)Ljava/la
ng/StringBuilder;
   26:  invokevirtual   #34; //Method java/lang/StringBuilder.toString:()Ljava/l
ang/String;
   29:  astore_2
   30:  getstatic       #38; //Field java/lang/System.out:Ljava/io/PrintStream;
   33:  aload_2
   34:  invokevirtual   #44; //Method java/io/PrintStream.println:(Ljava/lang/St
ring;)V
   37:  return
 
这个是经JVM编译后产生的字节码信息,这里我们只需要关心//后面的注释就可以了。
由上面展示的我们可以看到,在上面程序执行的内部,其实是有JVM帮我们产生了已StringBuilder的对象用来创建一个String,然后通过四次调用append方法来来把那些小段字符串拼接到一起。JVM之所以会自行生成一个StringBuilder对象,是因为它是更有效率的。
再来看下面的例子:
public class WhitherStringBuilder {
	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();
	}
}
 
在第一个方法implicit中使用的是String,用+ += 操作符拼接字符;第二个方法explicit中使用的是StringBuilder,用append方法追加字符,下面我们依旧用javap命令来查看这两个方法的内部执行机理.
public java.lang.String implicit(java.lang.String[]);
  Code:
   0:   ldc     #16; //String
   2:   astore_2
   3:   iconst_0
   4:   istore_3
   5:   goto    32
   8:   new     #18; //class java/lang/StringBuilder
   11:  dup
   12:  aload_2
   13:  invokestatic    #20; //Method java/lang/String.valueOf:(Ljava/lang/Objec
t;)Ljava/lang/String;
   16:  invokespecial   #26; //Method java/lang/StringBuilder."<init>":(Ljava/la
ng/String;)V
   19:  aload_1
   20:  iload_3
   21:  aaload
   22:  invokevirtual   #29; //Method java/lang/StringBuilder.append:(Ljava/lang
/String;)Ljava/lang/StringBuilder;
   25:  invokevirtual   #33; //Method java/lang/StringBuilder.toString:()Ljava/l
ang/String;
   28:  astore_2
   29:  iinc    3, 1
   32:  iload_3
33:  aload_1
   34:  arraylength
35:  if_icmplt       8
  38:  aload_2
   39:  areturn

public java.lang.String explicit(java.lang.String[]);
  Code:
   0:   new     #18; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #45; //Method java/lang/StringBuilder."<init>":()V
   7:   astore_2
   8:   iconst_0
   9:   istore_3
   10:  goto    24
   13:  aload_2
   14:  aload_1
   15:  iload_3
   16:  aaload
   17:  invokevirtual   #29; //Method java/lang/StringBuilder.append:(Ljava/lang
/String;)Ljava/lang/StringBuilder;
   20:  pop
   21:  iinc    3, 1
   24:  iload_3
   25:  aload_1
   26:  arraylength
   27:  if_icmplt       13
   30:  aload_2
   31:  invokevirtual   #33; //Method java/lang/StringBuilder.toString:()Ljava/l
ang/String;
  34:  areturn
 

从两分字节码可以看出,第二个的更短小简洁,并且第一个在每个循环中都会创建一个StringBuilder对象,而第二个方法始终只有一个StringBuilder对象。然而这两者的性能差异确实有很大差别的。
所以,当我们的连接操作很简单的时候,可以使用String,但是若涉及到循环,那么最好使用StrignBuilder.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值