Java中String、StringBuffer和StringBuilder的区别

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

一:String

1:String类的特性

1)String是个final类:所以String类是不可以被继承的。同样,String对象的值是不可以被改变的;

2)String对象的创建方法比较(原理比较):

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

1、字面常量方式创建String变量:String str1=“string” ;

String str2="string";

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

2、通过new关键字创建String对象:String str1=new String("string");

String str2=new String("string");

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

3、通过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+str2、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;

原文地址:https://www.cnblogs.com/huangdabing/p/9249248.html

时间: 2024-10-28 11:26:48

Java中String、StringBuffer和StringBuilder的区别的相关文章

【Java】String,StringBuffer与StringBuilder的区别??

String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以

Java中String,StringBuffer与StringBuilder的差别

String 字符串常量: StringBuffer 字符串变量〈缓冲区〉(线程安全): StringBuilder 字符串变量〈缓冲区〉(非线程安全): 简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响

Java中string拼接,StringBuilder,StringBuffer和+

Java中string拼接,StringBuilder,StringBuffer和+,到底哪个更合适? StringBuilder线程不安全,效率较线程安全的StringBuffer高.jdk1.5之前,+操作会产生大量String对象,影响GC的效率,但是jdk1.5之后做了优化,使用+操作符不一定会产生大量String对象,而是自动优化为StringBuilder方式.如果是在一个for循环中进行String拼接,还是不建议使用+操作,因为会大量产生new StringBuilder()对象

String,StringBuffer与StringBuilder的区别?-转

String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简 要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了

String,StringBuffer与StringBuilder的区别,线程安全,非线程安全

String,StringBuffer与StringBuilder的区别?? 转载地址:http://blog.csdn.net/rmn190/article/details/1492013 另外:StringBuilder的实现与技巧 : http://lixianhuei.cnblogs.com/archive/2005/12/27/305708.html 对线程安全 ,非线程安全的讨论: 怎么理解Stringbuffer 是线程安全的 stringbuilder是线程不安全的? http:

java中String类、StringBuilder类和StringBuffer类详解

本位转载自http://www.cnblogs.com/dolphin0520/p/3778589.html 版权声明如下: 作者:海子 出处:http://www.cnblogs.com/dolphin0520/ 本博客中未标明转载的文章归作者海子和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利 正文: 探秘Java中String.StringBuilder以及StringBuffer 相信String这个类是Java中使用得

java中的StringBuffer和StringBuilder

/* java.lang.StringBuffer; java.lang.StringBuilder; 1.StringBuffer和StringBuilder是什么? 是一个字符串缓冲区. 2.工作原理 预先在内存中申请一块空间,以容纳字符序列, 如果预留的空间不够用,则进行自动扩容,以 容纳更多字符序列. 3.StringBuffer,StringBuilder  和  String最大的区别? String是不可变得字符序列,存储字符串常量池中. StringBuffer底层是一个char

java中string , StringBuffer , StringBuilder 区别

1.String String变量的值不能改变,如果要改变String变量的值,虚拟机首先会遍历方法区中的字符串常量,如果存在需要的值,则虚拟机直接把此常量值的地址分配给String变量,如果不存在这样的值,虚拟机则会另外在方法区中划分出一块内存空间存储字符串常量,然后把这块地址赋给String变量. 因此,String类的内容声明后不可被改变,改变的只是其内存地址的指向. 2.StringBuffer StringBuffer是使用缓冲区存储字符串对象的,对象的内容可以改变.并且是线程安全的.

JAVA String,StringBuffer与StringBuilder的区别??

String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简要的说, String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以

java中String&StringBuffer&StringBuilder

1. String,字符串常量 StringBuffer,字符串变量(线程安全) StringBuilder,字符串变量(非线程安全) 2.String and StringBuffer String和StringBuffe的主要区别在于String是不可变对象,每次对String进行改变的时候其实 等于生成了一个新的String对象,因此经常改变内容的字符串不要使用String类型,因为每次形成新的对象都会对系统的性能产生影响,特别当内存中的无引用的对象多了之后,JVM的GC就会开始工作,那速