Java 從陣列來看值傳遞和引用傳遞

牛家俊發表於2021-01-29

從陣列來看值傳遞和引用傳遞

慣例先看一段程式碼

public class DemoCollection14 {
    public static void main(String[] args) {

        String [] strs = {"zs", "ls", "wu"};

        for (String str : strs) {
            strs[0] = null;
            System.out.println(str);
        }

        
        for (String str : strs) {

            System.out.println(str);
        }
    }
}

//輸出:
//		zs
//		ls
//		wu
//
//		null
//		ls
//		wu
要想搞懂這道題,先看下面講解

重新學習陣列(此處引用了廖雪峰老師的講解)

基本型別陣列

陣列是引用型別,並且陣列大小不可變

public class Main {
    public static void main(String[] args) {
        // 5位同學的成績:
        int[] ns;
        ns = new int[] { 68, 79, 91, 85, 62 };
        System.out.println(ns.length); // 5
        ns = new int[] { 1, 2, 3 };
        System.out.println(ns.length); // 3
    }
}

陣列大小變了嗎?看上去好像是變了,但其實根本沒變。

對於陣列ns來說,執行ns = new int[] { 68, 79, 91, 85, 62 };時,它指向一個5個元素的陣列:

     ns
      │
      ▼
┌───┬───┬───┬───┬───┬───┬───┐
│   │68 │79 │91 │85 │62 │   │
└───┴───┴───┴───┴───┴───┴───┘

執行ns = new int[] { 1, 2, 3 };時,它指向一個新的3個元素的陣列:

     ns ──────────────────────┐
                              │
                              ▼
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│   │68 │79 │91 │85 │62 │   │ 1 │ 2 │ 3 │   │
└───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘

但是,原有的5個元素的陣列並沒有改變,只是無法通過變數ns引用到它們而已。

字串陣列

如果陣列元素不是基本型別,而是一個引用型別,那麼,修改陣列元素會有哪些不同?

字串是引用型別,因此我們先定義一個字串陣列:

String[] names = {
    "ABC", "XYZ", "zoo"
};

對於String[]型別的陣列變數names,它實際上包含3個元素,但每個元素都指向某個字串物件:

          ┌─────────────────────────┐
    names │   ┌─────────────────────┼───────────┐
      │   │   │                     │           │
      ▼   │   │                     ▼           ▼
┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┐
│   │░░░│░░░│░░░│   │ "ABC" │   │ "XYZ" │   │ "zoo" │   │
└───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┘
      │                 ▲
      └─────────────────┘

names[1]進行賦值,例如names[1] = "cat";,效果如下:

          ┌─────────────────────────────────────────────────┐
    names │   ┌─────────────────────────────────┐           │
      │   │   │                                 │           │
      ▼   │   │                                 ▼           ▼
┌───┬───┬─┴─┬─┴─┬───┬───────┬───┬───────┬───┬───────┬───┬───────┬───┐
│   │░░░│░░░│░░░│   │ "ABC" │   │ "XYZ" │   │ "zoo" │   │ "cat" │   │
└───┴─┬─┴───┴───┴───┴───────┴───┴───────┴───┴───────┴───┴───────┴───┘
      │                 ▲
      └─────────────────┘

這裡注意到原來names[1]指向的字串"XYZ"並沒有改變,僅僅是將names[1]的引用從指向"XYZ"改成了指向"cat",其結果是字串"XYZ"再也無法通過names[1]訪問到了。

對“指向”有了更深入的理解後,試解釋如下程式碼:

public class Main {
    public static void main(String[] args) {
        String[] names = {"ABC", "XYZ", "zoo"};
        String s = names[1];
        names[1] = "cat";
        System.out.println(s); // s是"XYZ"還是"cat"?
    }
}

//輸出"XYZ"

//解釋原因:
//names字串陣列建立好後,s指向names[1]這個位置。
//但是呢,name[1]可不是把原資料XYZ更改為了cat,而是重新指向了存有cat的那個空間地址。
//故s還是指向原來那個位置,故輸出的是XYZ

再回到開始的那個問題:

public class DemoCollection14 {
    public static void main(String[] args) {

        String [] strs = {"zs", "ls", "wu"};

        for (String str : strs) {
            strs[0] = null;
            System.out.println(str);
        }

        
        for (String str : strs) {

            System.out.println(str);
        }
    }
}

//輸出:
//		zs
//		ls
//		wu
//
//		null
//		ls
//		wu

foreach,其實就是迭代器。迭代器,不是傳統意義的for迴圈輸出陣列資料。
    而是定義了String str,依次str=strs[i],並輸出str。故和前面的xyz,性質一樣了。

相關文章