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的開發人員幫我們想到了,我們知道就可以了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章