---
# System prepended metadata

title: 變數之六 - 字串
tags: [memory, java, string, stack, article index, tutorial article, teaching, heap]

---

記憶體有兩個區塊，一個叫做 stack, 一個叫做 heap。
當我們宣告一個字串 String str = "123"; 時，

程式就會把 str 名稱記在 stack 裡面，再把 123 這個值記在 heap 裡面。
然後將 str 跟 123 做一個關聯性的參考。
（當然實際上是更複雜的，這邊先簡化敘述讓你比較好理解）

因此我們印出 str 時，程式就會參照這個關聯將 123 印出來。
![](https://i.imgur.com/ad6Azqi.png)

但若我們想要覆蓋過去原本的值時，像以下這樣的例子。
str = str + "abc";

這個時候我們 str 就會指向新的位置，其值就會變成 123abc 了。
記憶體的變化會像是這樣的狀態。
![](https://i.imgur.com/khtzQ4t.png)

這個時候可能會有疑問，被取消關聯的 123 會怎麼樣？

當程式中沒有任何參考(reference)指向他的話，
這個放置著 123 的 heap 記憶體位置，就會被當成垃圾，
當 Java 執行垃圾回收(Garbage Collection)時，這塊記憶體就會釋放掉。

這就是字串串接時，記憶體部分會產生的動作。
也由於字串是常數的關係，所以任何會改變內容的行為(串接、取代、刪除等)，
在記憶體之中都是以類似方法操作物件。

---

然後假設有一個狀況是這樣，

```java
public static void main(String[] args) {
    String str = "";
    for (int i = 0; i < 10; i ++) {
        str = str + i;
    }
    System.out.println(str);    //會印出 0123456789
}
```
那麼記憶體的變動就大概如以下圖片這樣子的。
![](https://i.imgur.com/IeupLyd.png)
這個時候可以看見其實 heap 會有很多沒有被參考到的記憶體位置存在，
需要等到 Java 的垃圾回收（GC）後才會釋放這些記憶體。

但其實 GC 是由程式控管，我們無法主動呼叫 GC，也沒辦法得知 GC 何時會發生。
且 GC 發生時也會稍稍的影響程式的運作效能。

所以一般來說，如果有大量的字串需要用拼湊的方式建立的話，
其實不建議直接操作字串，因為程式會產生大量待回收的垃圾。

```java
public static void main(String[] args) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 10; i ++) {
        sb.append(i);
    }
    System.out.println(sb.toString());    //會印出 0123456789
}
```
所以，如果真的有需要拼湊字串的需求時，
我們會建議使用 StringBuilder 或者 StringBuffer 之類的物件，
當我們想要操作字串內容時，
這些程式的做法是，會先將拼湊的字串內容置於一個緩衝區之內，
等到全部拼湊完且將其轉換成字串後，才真正的會將這個字串置於 heap 內。

像這樣的做法就不容易產生大量的垃圾了。


