String,StringBuffer,StringBuilder的区别

String,StringBuffer,StringBuilder的区别可能是面试中java基础中最多的问题了。我记得我刚实习那会面试的时候就记得这三个的速度的速度比是StringBuilder>StringBuffer>String,还是死记硬背的,知其然而不知其所以然。今天就写篇博客正式总结下这三个的区别。
首先来看下String的部分源码(后面的代码都是以jdk1.7为准)

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage.字节数组 */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

    /** use serialVersionUID from JDK 1.0.2 for interoperability */
    private static final long serialVersionUID = -6849794470754667710L;

可以看到String底层是用字节数组实现的,而且用了final修饰,所以说String对象是不可变的。为什么不可变呢,有很多种原因,包括:1. 字符串常量池的需要
2.缓存HashCode
3.线程安全
有兴趣的可以参照这篇文章http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/

在看看StringBuilder,类的继承关系如图
这里写图片描述

 public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

StringBuilder类,继承了AbstractStringBuilder抽象类,来看下AbstractStringBuilder的部分源码

 abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * This no-arg constructor is necessary for serialization of subclasses.
     */
    AbstractStringBuilder() {
    }

可以看到StringBuilder的底层实现其实也是字节数组char[] ,和String一样。那他们俩有什么不一样呢?
我们来看下关对StringBuilder进行操作的部分源码,

public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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

    // Appends the specified string builder to this sequence.
    private StringBuilder append(StringBuilder sb) {
        if (sb == null)
            return append("null");
        int len = sb.length();
        int newcount = count + len;
        if (newcount > value.length)
            expandCapacity(newcount);
        sb.getChars(0, len, value, count);
        count = newcount;
        return this;
    }


    public StringBuilder append(CharSequence s) {
        if (s == null)
            s = "null";
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof StringBuffer)
            return this.append((StringBuffer)s);
        if (s instanceof StringBuilder)
            return this.append((StringBuilder)s);
        return this.append(s, 0, s.length());
    }

    /**
     * @throws     IndexOutOfBoundsException {@inheritDoc}
     */
    public StringBuilder append(CharSequence s, int start, int end) {
        super.append(s, start, end);
        return this;
    }

    public StringBuilder append(char[] str) {
        super.append(str);
        return this;
    }

可以看到append方法其实都调用父类AbstractStringBuilder的append方法

public AbstractStringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }


 public AbstractStringBuilder append(String str) {
        if (str == null) str = "null";
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }    
    public AbstractStringBuilder append(StringBuffer sb) {
        if (sb == null)
            return append("null");
        int len = sb.length();
        ensureCapacityInternal(count + len);
        sb.getChars(0, len, value, count);
        count += len;
        return this;
    }

可以看到这三个重载的方法都是对字节数组的操作。

那String类的操作呢?
对String类进程操作都是通过加号“+”,Think in Java里是这样介绍“+”号的:
这里写图片描述
这里写图片描述
这里写图片描述
可以看到单行String的第一个“+”操作代码会生成StringBuilder对象,然后调用其append()方法。

@Test
    public void testCirCle(){
        String[] strings={"1","2","3"};
        String result = "";
        for(int i=0;i<strings.length;i++){
            result = result + strings[i];//每次都会生成一个StringBuilder对象
        }
         System.out.println(result);
    }

若在for循环中使用“+”,则每次循环都会生成一个StringBuilder对象。
注意有一种特殊的情况

 @Test
    public void testPlus(){
        String stringPlus="1"+"2"+"3"+"4";
        System.out.println(stringPlus);
    }

这种情况生成几个StringBuilder?一个?四个?
我们去看下反编译后的这段代码的class源码

这里写图片描述
可以神奇的看到没有并没有生成一个StringBuilder对象,原来是JVM虚拟机编译代码时在这种全为String的“+”操作情况下进行了优化。
那StringBuffer又有什么不同呢?
首先先看StringBuffer的源码
这里写图片描述

/*
* @author      Arthur van Hoff
 * @see     java.lang.StringBuilder
 * @see     java.lang.String
 * @since   JDK1.0
 */
 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

从类的继承关系和源码可以看出StringBuffer和StringBuilder基本一致,主要的区别在于字符串的操作方法。

public synchronized StringBuffer append(Object obj) {
        super.append(String.valueOf(obj));
        return this;
    }

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


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

可以StringBuffer的append方法调用的也是父类AbstractStringBuilder的append()方法,只不过其方法用了synchronized修饰,所以其字符串操作是线程安全的。从jdk1.5之后引入的StringBuilder的append()去掉了synchronized关键字,所以StringBuilder的append方法是线程不安全的,但是其节省了同步的资源,所以StringBuilder的append方法要快于StringBuffer的append方法。
总结

String,StringBuffer,StringBuilder的区别
1、string是不可变的,StringBuffer,StringBuilder是可变的,String的每一次操作都是用StringBuilder实现的。
2、String和StringBuffer是线程安全的,StringBuilder是线程不安全的。
3、在字符串的操作速度上,StringBuilder>StringBuffer>String

三者的应用场景
1、字符串定义一次,后面不会对其改变,用String
2、定义字符串后需要对其进行操作,则用StringBuilder,如果有多线程操作字符串的需求则用StringBuffer。

PS:希望这篇博客能帮助一些人。其实这里面还有很多详细的知识点,例如String常量池啊,线程安全的详细描述啊,等等,限于篇幅有限没有过多描述。如果有写的不对的地方,还请大家留言指正和交流讨论。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值