對於final關鍵字和concat()方法的思考

前置

首先我們都知道final關鍵字修飾的變量必須初始化,且值不能改變。如果是基本類型則值不能改變,如果是引用類型,則引用地址不能改變,但是這個引用所指向的對象裏面的內容還是可以改變的。
我們先來猜猜看,以下哪一句會通不過編譯器編譯:

public class FinalBean {
    private final int i = 0;
    private final int j;
    private final String name = "";

    public FinalBean(){
        j = 1;
        this.name.concat("123");
        this.name = "123";
    }
}
this.name = "123";

這句,記住final的原理即可理解,那爲什麼this.name.concat(“123”);不會報錯呢,因爲底層實現是返回一個新的String對象。

看看concat()底層源碼

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

由源碼知道string是由final修飾的,並且其中的char數組value[](以字符數組的形式用來保存字符串)也是由final修飾的。但是我們知道數組是引用變量類型,當final修飾引用變量的時候,變量中保存的地址是不能更改的,但是指針指向的數組本身是可以更改的。如果分析到這裏有錯的話請指出,沒錯的話接着看核心問題。

  public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }

當我們調用concat方法試圖從一個字符串後面增添字符,根據這段源碼。它先是通過Array.copyOf()方法new出一個長度合適的新字符數組。

public static char[] copyOf(char[] original, int newLength) {
        char[] copy = new char[newLength];
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

然後可以看到,調用了System類中的arraycopy()方法。

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

最後調用了這個native方法來進行數組拷貝,所以爲什麼要採用這樣一個思路來處理string的concat操作?(其實可以拓展到所有對string的改變操作,這也是我真正想弄懂的地方,只是用concat來描述問題比較方便。)
特別是前面提到的,既然final修飾並不能限制數組本身的改變,爲什麼不對字符數組進行修改以達到字符串內容的修改?就算指針受final限制而不能更改,但這樣的方式一樣可以達到改動string的目的不是嗎?

我的思考

1、

我真特麼蠢,剛纔瞄了一眼AbstractStringBuilder源碼就想通了,我提出的這個問題恰好迎合了StringBuffer或StringBuilder的目的。啥都被你String做完了,還要別人來幹嘛?況且你String還有更多其它的用處呢。

以append方法爲例。它就是採用的擴容數組、然後拷貝字符串數組、直接返回當前字符數組。從而達到不產生新對象的目的。

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;
    }

2、

還有更重要的一點被我忽略了。數組的大小是不可變的,特別是concat等會改變被保存字符串長度的操作,更是無法說通。因此這種情況下必須重新new一個字符數組,而原數組變量不能改變指針的指向(已經被final修飾),因此必須要生成一個新的string對象,調用相應構造方法,將字符數組數據傳進去。

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