Java中String、StringBuffer和StringBuilder的区别

转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6581009.html html

    在编程中,对于字符串拼接,咱们能够用String类重载的+或concat(str)、StringBuffer的append(str)、StringBuilder的append(str)。那么,三者到底有什么不一样呢?编程

   

一:String

        1:String类的特性

             1)String是个final类:因此String类是不能够被继承的。一样,String对象的值是不能够被改变的;数组

             2)String对象的建立方法比较(原理比较):安全

                 在JVM加载运行class文件时,对于字节文件中出现的常量(符号引用、字符串字面量等)会在方法区的常量池中分类存放。其中,源代码中出现过的字符串字面常量会保存在 CONSTANT_String_info常量表中。而后,根据不一样的建立方法,会有不一样的内容分配,具体以下:多线程

                  一、字面常量方式建立String变量:String str1=“string” ;app

                                                              String str2="string";性能

                      上面建立了两个String引用,都指向一样内容的字符串。咱们知道该类在加载时,字符串常量被存放在 CONSTANT_String_info常量表 中,而且同一字符串内容只会存放一次(咱们称常量表中的字符串为“拘留字符串”)。故此,上面两个String引用都指向 CONSTANT_String_info常量表 中的同一地址(同一拘留字符串)。所以,这里 str1==str2 返回true。(另:这里是没有建立string对象的,由于没有在堆中分配内存)ui

                  二、经过new关键字建立String对象:String str1=new String("string");this

                                                                  String str2=new String("string");spa

                       类在加载时,首先把源代码中出现的字符串常量放在CONSTANT_String_info常量表中,故此:上面两语句中相同的 “string” 常量值放在了字符串常量表中同一处。而后,在执行到这两条语句时,根据new关键字,在堆中分别建立String对象str1和str2,而且两对象在堆中保存值均为 "string" 。也就是说,此时JVM中有三个地方存有同一字符串值:常量表中的拘留字符串、str1对象的堆空间、str2对象的堆空间。所以,这里的 str1==str2 返回的是false。调用String类重写过的equals()方法比较两对象的值才返回true。

                  三、经过intern()方法建立的String“对象”:String str1="string";

                                                                         String str2=new String("string").intern();

       intern()的做用:当常量池的字符串常量表中存在与 "string" 相同(内容相同)的字符串(拘留字符串)时,则直接返回这个拘留字符串的地址,赋值给str2(此时没有建立新的对象);

       若是常量池中没有与 “string” 相同的拘留字符串时,则把 "string" 添加到常量池中(成为拘留字符串,供下一个intern()时做返回用),并在堆中建立String对象而后把对象的引用返回(此时有新对象建立);

       所以,这里 str1==str2 返回true。

       因而可知,intern()方法主要能够用来避免建立内容重复的String对象,值得咱们重视。

 

        2:String中重载的 + 工做原理

        用String类重载的 + 拼接字符串时,能够在 + 先后跟其余数据类型,不必定是string类型。其余类型数据会自动“向高看齐”转化为String类型。

        重载的 +  操做符,实际上是建立一个StringBuffer或StringBuilder对象,用append方法对字符串进行链接,最后调用toString方法返回String字符串。 

        注意用 + 拼接字符串的两种状况:

              2.1)用 + 拼接两个字符串变量:String str1="str";

                                                        String str2="ing";

                                                        String str=str1+str2;

                                                        String str1_2="string";

                      解析:这里用 + 拼接的是两个字符串变量,因此会首先建立一个StringBuffer/StringBuilder,而后 append(str1).append(str2) 把str1和str2拼接起来,最后经过toString()生成一个新的String对象并把引用返回,赋值给str。因此,这里的 str==str1_2 结果是false。这里建立了新的String对象。

             2.2)用 + 拼接两个字符串字面量:String str1="str"+"ing";

                                                         String str2="string";

                      解析:用 + 拼接两个字符串字面量时,JVM会自动把这两个字面量的合并值做为一个完成的字符串常量值,保存到常量池的字符串常量表中。所以,这里 str1==str2 结果是true。这里没有建立新的String对象,只是把拼接结果做为拘留字符串的保存地址返回了而已。

 

        3:String中的concat(str)拼接字符串

        与 + 能够拼接其余数据类型不一样,concat()只能把string类型的内容拼接到调用者后面。

        经过查看源码

public String concat(String str) {      
int otherLen = str.length();      
if (otherLen == 0) {          
return this;  
    }  
    char buf[] = new char[count + otherLen];  
    getChars(0, count, buf, 0);  
    str.getChars(0, otherLen, buf, count);  
    return new String(0, count + otherLen, buf);  
    }  

         咱们能够看到,concat()拼接字符串的原理是建立一个新的char[]字符数组,把两个字符串转化为char后存在新数组中,最后用char[]建立一个新的String对象。

 

二:StringBuilder

        1:StringBuilder是个非线程安全的final类,不能被继承

        2:StringBuilder的append() 工做原理

public StringBuilder append(String str) {
        super.append(str);
        return this;
    }

        StringBuilder是调用了其父类的append方法的,咱们来看源码:

  public AbstractStringBuilder append(String str) {
        if (str == null) {
            str = "null";
        }
        int len = str.length();
        if (len == 0) {
            return this;
        }

        int newCount = count + len;//统计拼接后字符串长度
        if (newCount > value.length) {
            expandCapacity(newCount);//若是拼接结果大于所用的char[],则扩容
        }
       //getChars将拼接字符串赋值到char[]中
        str.getChars(0, len, value, count);
        count = newCount;//更新char[]所保存的字符串长度
        return this;
    }

         可知,用StringBuilder拼接字符串时,实际上是在底层建立了一个char[]数组,而后经过char[]把要拼接的字符串添加到char[]而已。最后经过toString()生成最终拼接结果时就是经过  return new String(char[]) 实现的。

 

三:StringBuffer

        1:StringBuffer是个线程安全的final类,不能被继承。

        2:StringBuffer的 append() 工做原理

public synchronized StringBuffer append(String str) {
    super.append(str);
        return this;
    }

        能够看到,StringBuffer的append也是调用父类AbstractStringBuilder的append方法实现的,原理同StringBuilder。其惟一不一样的地方在于,加了一个syncrhoized关键字修饰append()方法,保证了线程同步。

 

四:三者性能比较与选用

        1:性能:通常来讲是  StringBuilder>StringBuffer>String

            从上面四种(其实应该说是五种,+ 分为字符串常量的拼接和变量的拼接两种)的字符串拼接来看,除了字符串常量的拼接是返回拘留字符串的地址外,其余四种(str1+str二、str1.concat(str2)、builder.append(str1).append(str2)、buffer.append(str1).append(str2))都是使用了StringBuilder,或者说是StringBuilder的父类的拼接方法来作的——建立一个char数组,把须要拼接的内容先存进char数组,最后经过char数组建立新的String对象返回。

            形成三者性能差异的主要缘由是:

            用String的 + 累加拼接字符串变量时,每拼接一个就会建立一个StringBuilder,并经过append()方法拼接,而后返回一个新的String对象。而后再继续拼接下一个变量。这样就会致使重复建立StringBuilder对象,性能低下。用 concat() 累计拼接时,则每两个字符串拼接都会建立一个 char[] 进行内容拼接并返回一个新的String对象做为结果,重复调用concat()会致使重复建立char[]和新String对象,性能低下。

            StringBuilder在调用toString()以前都不会建立拼接结果,而且底层的char数组会自动扩容,一直到拼接字符串所有存入char数组后,调用toString()时才建立新的String对象并返回,这样就避免了重复建立,效率提升。

            StringBuffer则由于使用了syncrhoized对append()进行了加锁,因此致使性能稍微低于StringBuilder。

        2:不一样情景下的选用

             拼接两个字符串字面量,用 + 操做符;

             单线程下拼接两个字符串变量,用StringBuilder;  

             多线程下拼接两个字符串变量,用StringBuffer;

相关文章
相关标签/搜索