String, StringBuilder, StringBuffer深入解析

网上太多讲解String, StringBuilder, StringBuffer的区别了,但是大多数都浅尝而止,大多数只是提到String是字符串常量,因为它是常量,所以也是线程安全的,StringBuilder和StringBuffer的区别是后者是线程安全的,还有很多细节没有介绍到,尽自己之能补充一下。

先上一张类结构图。
在这里插入图片描述

可变性

总所周知,String类是一个不可变类也就是final 符描述,难道final符描述的对象就不可变了吗?答案并不是,final描述标识引用不可变,但是引用下面的内存信息还是可以变化的,String不可变的一个根本原因是String根本没有提供操作它的内部对象的方法,以及它内部的char[]也加上final符,进而就变成不可变了。StringBuffer与StringBuilder都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,提供了各种append的方法,所以他们俩是可变类。

初始化

初始化就很简单了,String 只能通过new String(“hello”)方式进行初始化,String s = “hello”,这种方式如果常量池里有这个对象,它只是赋引用,并没有进行初始化。
同样,StringBuffer与StringBuilder也是只能通过new 的方式来初始化。

修改值

String是不可变的,所以不存在修改的概念,但是下面的这些代码看着像是修改,实际上是个语法糖,最终还是会转换为StringBuilder进行append。

String str = "hello";
str += "java";

语法糖解析之后变为:

StringBuffer sb = new StringBuffer("hello");
sb.append("java");
str = sb.toString();

所以,没有循环,判断或者其他作用域阻断的情况下,尽情的用+吧,没有问题的,不用担心性能问题(1.5之后)。
StringBuffer与StringBuilder可以通过append来进行修改。
StringBuffer与StringBuilder的append的扩容过程是怎样的?直接看源码吧。

    /**
     * Appends the specified string to this character sequence.
     * <p>
     * The characters of the {@code String} argument are appended, in
     * order, increasing the length of this sequence by the length of the
     * argument. If {@code str} is {@code null}, then the four
     * characters {@code "null"} are appended.
     * <p>
     * Let <i>n</i> be the length of this character sequence just prior to
     * execution of the {@code append} method. Then the character at
     * index <i>k</i> in the new character sequence is equal to the character
     * at index <i>k</i> in the old character sequence, if <i>k</i> is less
     * than <i>n</i>; otherwise, it is equal to the character at index
     * <i>k-n</i> in the argument {@code str}.
     *
     * @param   str   a string.
     * @return  a reference to this object.
     */
    public AbstractStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len);
        str.getChars(0, len, value, count);
        count += len;
        return this;
    }

数组的扩容的方法在ensureCapacityInternal中。

    /**
     * For positive values of {@code minimumCapacity}, this method
     * behaves like {@code ensureCapacity}, however it is never
     * synchronized.
     * If {@code minimumCapacity} is non positive due to numeric
     * overflow, this method throws {@code OutOfMemoryError}.
     */
    private void ensureCapacityInternal(int minimumCapacity) {
        // overflow-conscious code
        if (minimumCapacity - value.length > 0) {
            value = Arrays.copyOf(value,
                    newCapacity(minimumCapacity));
        }
    }

最后调用newCapacity进行扩容。

    /**
     * Returns a capacity at least as large as the given minimum capacity.
     * Returns the current capacity increased by the same amount + 2 if
     * that suffices.
     * Will not return a capacity greater than {@code MAX_ARRAY_SIZE}
     * unless the given minimum capacity is greater than that.
     *
     * @param  minCapacity the desired minimum capacity
     * @throws OutOfMemoryError if minCapacity is less than zero or
     *         greater than Integer.MAX_VALUE
     */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
            ? hugeCapacity(minCapacity)
            : newCapacity;

先扩容之前的长度的2倍+2,如果不够的话,直接使用入参的长度,目前还没想通为什么要这么做,直接使用入参的长度不就好了么?可能是为了减少扩容次数。

equals()和hashCode()

String实现了equals()方法和hashCode()方法
而StringBuffer和StringBuilder并没有实现equals()方法和hashCode()方法,因此不能使用StringBuffer对象存储进Java集合中,会达不到想要的效果。

线程安全

String和StringBuffer是线程安全的,因为String是常量而StringBuffer的所有方法都通过synchronized符进行标识,StringBuilder不是线程安全的。
所以在使用上遵循以下几点:

1.如果要操作少量的数据用 String
2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

效率上:
StringBuilder > StringBuffer > String

然后我想吐槽的是StringBuffer真的有人用么,在业务代码里,我从来没有见到过,框架代码可能会用到,java的开发人员帮我们想到了,我们知道就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值