[Java基础]深入探讨String、StringBuffer与StringBuilder的区别

来源:互联网 发布:如何在淘宝上找客服 编辑:程序博客网 时间:2024/06/05 17:51

关于 String,StringBuffer 和 StringBuilder 的区别,相信每个初级 Java 工程师在前几次面试的时候都会被问到,当然,我也不例外。那么,我们应该怎样回答这个问题呢,又或者说他们三者在我们的技术基础结构中到底有多饱满?

一个普通的,经过培训或者自学不精的初级工程师可能是这样回答的:

String 创建的是一个不可变的字符串对象,如果对字符串进行修改那么就会创建一个新的String对象,并把当前引用指向这个对象; StringBuffer 和 StringBuilder 都是可变的字符串对象,其中引用指向的字符串对象内容的增加或者减少不会像 String 一样创建新的对象,只不过对 StringBuffer 的操作是线程安全的, StringBuilder 的操作是不安全的。

如上,这样的回答对么?对的,是正确的,但是看上去总感觉少点什么,感觉不够丰满。所以,这个时候我们就要开始祭出我们的大杀器“源码”来将这个问题深入,探讨一下造成以上结果的根本原因。

谁也不能阻挡我装逼的脚步

为什么 String 对象是不可变的呢,因为 String 类是被final修饰的。[斜眼笑]

public final class String //咳咳,兄弟,放下手中的菜刀,有话好好说

嘿嘿嘿,好了,我们重点来说一下StringBuffer和StringBuilder。
在JDK1.5版本之前,是没有 StringBuilder 的,而在1.0版本开始就有了StringBuffer。这时候的 StringBuffer 可以算是一心一意为了线程服务,几乎所有的方法都被* synchronized *关键字进行修饰,以append(String str)举例。

 @Override  //这是因为在JDK1.5版本之后,继承AbstractStringBuilder public synchronized StringBuffer append(String str) {        toStringCache = null;//toStringCache被设置为null的话,JVM会对其先进行缓存,这个我们以后再说        super.append(str); //调用父类中的方法        return this;}

在JDK1.5版本的时候,Sun 的工程师们可能是认为很多时候不需要 StringBuffer 参与到多线程任务中来,而在单线程中* synchronized 会影响虚拟机的执行效率,于是提出了在单线程中使用的,几乎所有方法都与 StringBuffer 相同的类 StringBuilder。然后把两者之间相同的地方抽象出来成为了 AbstractStringBuilder *。这时我们来看一下 StringBuilder 中的 append 方法

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

在 AbstractStringBuilder 中,append(String str) 这个方法是这样实现的:

public AbstractStringBuilder append(String str) {        if (str == null)            return appendNull();        int len = str.length();        ensureCapacityInternal(count + len);//扩容        str.getChars(0, len, value, count);//StringBuffer和StringBuilder底层是一个字符数组,这里是把str分解为字符数组再复制到StringBuffer或StringBuilder的字符数组中。        count += len;//count是这个对象的一个属性,代表着数组大小        return this; //返回的是当前对象    }

在上面只是对 StringBuffer 和 StringBuilder 线程安全性的一个对比,下面我们来说点别的,比如大小。
在初始化一个 StringBuffer 或者 StringBuilder 的时候,我们有三种方法,他们初始的大小都是多少呢? StringBuffer和StringBuilder 的构造规则一致,我们以StringBuffer举例。

1.默认(空参数)构造
    public StringBuffer() {        super(16);    }

可以看出,StringBuffer 在空参初始化的时候以参数为“16”调用了父类的构造方法

   AbstractStringBuilder(int capacity) {        value = new char[capacity];        //所以,StringBuffer创建了一个容量为“16”的字符数组。    }

所以说,有人问你无参构造 StringBuffer/StringBuilder 容量有多大的时候,会答了把~

2.有参(int capacity)构造
    public StringBuffer(int capacity) {        super(capacity);    }

额,和第一点差不多,不进行赘述了。

3.有参(String str)构造

这种情况我们平时是用的最多的 StringBuffer sb = new StringBuffer(“xxxx”);

public StringBuffer(String str) {        super(str.length() + 16);        append(str);}

看到没,+16了看到没!设定容量为 str.length()+16 之后,调用了 append 方法, 将str转换为字符数组进行存储。

以上就是 StringBuffer/StringBuilder 初始构造的容量,那么我们下面来说说每次扩容增长容量和最大容量。
在 AbstractStringBuilder.java 也就是 StringBuffer 和 StringBuilder 所继承的父类中,有这样一段代码

    void expandCapacity(int minimumCapacity) { //扩容方法        //新容量 = 原来容量*2 + 2         int newCapacity = value.length * 2 + 2;         if (newCapacity - minimumCapacity < 0)            newCapacity = minimumCapacity;  //因为Int存储机制的原因,在minimumCapacity超过一定范围之后,newCapacity 会为负数        if (newCapacity < 0) {            if (minimumCapacity < 0) // overflow 原容量范围过大,内存溢出                throw new OutOfMemoryError();            newCapacity = Integer.MAX_VALUE;//最大容量        }        value = Arrays.copyOf(value, newCapacity);    }

所以说,StringBuffer/StringBuilder 每次扩容的时候,正常的大多数情况下新容量为老容量的2倍+2,最大容量为 Integer.MAX_VALUE。

最后,这里有个傲娇的二维码在邀请你关注,里面有个萌萌哒的妹纸

感谢您的关注

最后的最后,祝大家身体健康~下篇再见
阅读全文
0 0
原创粉丝点击