String用“+"拼接過程


最近在研究String,對String用“+"拼接的過程很好奇,於是有了下面的描述。。。

本文先對兩個字符串拼接進行了研究,然后再對兩個字符串變量拼接進行,最后小結了一下,一起來看吧!!!

片段1:兩個字符串拼接

public class Test {
    public static void main(String args[]) {
        String str = "hello" + "world";
    }
}

用javap查看class文件結構

從文件結構中我們可以看出,"hello" + "world"直接被編譯成了"helloworld",並且將這個常量加載到操作數棧,再存儲到局部變量表,最后return結束。

public class Test {
    public static void main(String args[]) {
        String str1 = "hello" + "world";
        String str2 = "helloworld;
     System.out.print(str.equals(str));
    }
}

所以,上述代碼執行equals的結果是true,String str = "hello" + "world" 和 String str = "helloworld"的效果是一樣的。

 

延伸:上面講述的是直接把字符串賦值給變量,那new String()是一個怎樣的過程呢?

public class Test {
    public static void main(String args[]) {
        String str = new String("helloworld");
    }
}

用javap查看class文件結構

從文件結構中我們可以看出,首先不管如何會先new一個String,然后復制棧頂值且將該值壓入棧頂,初始化String,再存儲到局部變量表,最后return結束。

 

片段2:兩個變量拼接

public class Test {
    public static void main(String args[]) {
        String str = "hello";
        str = str + "world";
    }
} 

以上這段代碼會發生什么呢?

我們先來先看一下class文件

首先將變量str存儲局部變量表里,接着new StringBuilder(),並初始化,然后調用StringBuilder.append()方法拼接字符串,再通過StringBuilder.toString()調用new String()得到一個新的字符串變量,再將這個變量存儲到局部變量表,最后return結束。

所以代碼片段2的過程其實為new StringBuilder().append(str).append("world").toString()。

既然String用加號的拼接過程用到了StringBuilder的append和toString方法,不如看看這兩個方法的過程吧。

// StringBuilder的append方法
public StringBuilder append(String str) {
    // 調用父類AbstractStringBuilder的append方法
    super.append(str);
    return this; 
}    

public AbstractStringBuilder append(String str) {
     // 若字符串為空,返回"null"
     if (str == null)
        return appendNull();
     int len = str.length();
     // 獲取數組的大小,若是新的字符串的size大於char[]的length則new一個新的char[],並把原來的char賦值到新的char[]上
     ensureCapacityInternal(count + len);
     // 把str放入char[],底層調用本地方法System.arraycopy
     str.getChars(0, len, value, count);
     count += len;
     return this;
}

// 獲取容器大小
private int newCapacity(int minCapacity) {
    // 若新字符串 + 已存在的字符串的大小大於char[]的大小,則新容器的大小翻倍+2
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity - minCapacity < 0) {
          newCapacity = minCapacity;
     }
     // 若newCapacity<Integer.MAX_VALUE - 8則char[]大小取newCapacity;若Integer.MAX_VALUE- 8<newCapacity<Integer.MAX_VALUE,取則char[]大小取Integer.MAX_VALUE;若newCapacity>Integer.MAX_VALUE,拋錯。
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)? hugeCapacity(minCapacity): newCapacity;
}    

 

// 而StringBuilder的toString()調用的是new String()
public String toString() {
     return new String(value, 0, count);
}}    

public String(char value[], int offset, int count) {
    if (offset < 0) {
        throw new StringIndexOutOfBoundsException(offset);
     }
     if (count <= 0) {
        if (count < 0) {
                throw new StringIndexOutOfBoundsException(count);
         }
        if (offset <= value.length) {
            this.value = "".value;
            return;
            }
        }
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        // 根據拼接后的字符串長度new一個新的char[],並把原來的char[]數組的值復制到新的char[]上,節省內存
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }
}

 

小結

1、兩個字符串直接拼接時,編譯器對其進行了優化,此時String str = "hello" + "world"和String str = "helloworld"是一樣的;

2、由上述的分析可以兩個字符串的拼接的底層是通過StringBuilder的append方法先將字符串放在char[]數組中,再通過toString()調用new String()新建一個合適長度的char[],再把舊的char[]復制到新的char[]上完成的,而一個小小的”+“拼接的過程經過了3次System.arraycopy,過程復雜,每次“+”都會new StringBuilder又要toString,若是在for循環中使用這種方式,可謂是嗚呼哀哉,並且每次循環一次就會new StringBuilder(),若循環的次數非常多會new非常多的StringBuilder。

 

最后,歡迎大家留言。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM