探討Java中的值傳遞

方法(函數)的參數傳遞方式一共有兩種,分別是值傳遞和引用傳遞:

  1. 值傳遞:

方法調用時,實際參數把它的值傳遞給對應的形式參數,函數接收的是原始值的一個copy,此時內存中存在兩個相等的基本類型,即實際參數和形式參數,後面方法中的操作都是對形參這個值的修改,不影響實際參數的值。

2引用傳遞:

也稱爲傳地址。方法調用時,實際參數的引用(地址,而不是參數的值)被傳遞給方法中相對應的形式參數,函數接收的是原始值的內存地址;在方法執行中,形參和實參內容相同,指向同一塊內存地址,方法執行中對引用的操作將會影響到實際對象。

在Java中只有值傳遞,沒有引用傳遞。比如下面的例子:

    public static void add(int i){
        i = 2;
    }

在main方法中調用:

    int i = 1 ;
    add(i);
    System.out.println("i = " + i);

控制檯輸出:

 

結果是並沒有改變,所以顯而易見,調用add這個方法,傳入的是i的copy,在add中對i進行任何操作都不會影響原本的值。

 

那麼可能會有人問,爲什麼下面的例子就改變了原本的值呢?

Man類:

public class Man {

    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

修改名字的方法:

    public static void  updName(Man man ){
        man.setName("小鵬");
    }

在main方法中調用:

    Man man = new Man();
    man.setName("小明");
    System.out.println("調用函數之前的名字:" + man.getName());
    updName(man);
    System.out.println("調用函數之後的名字:" + man.getName());

控制檯輸出:

調用函數之前的名字是小明,調用之後就變成了小鵬,這不是改變了原來的值了嗎?

其實這裏是陷入了一個思想誤區,想弄清楚這個問題,首先要明白對象是什麼?

簡單來說,對象就是對一塊內存區域的引用,而這塊內存區域其實就是對象真正的值。

在通過Man man = new Man()創建對象的時候JVM都做了哪些操作?

簡單來說對象在被創建的時候,編譯器會在棧中爲Man man 劃分一塊區域,然後會在堆中爲對象的主體劃分一塊內存區域,並將該內存地址的引用返回給man。這樣就可以通過man中存儲的地址引用找到該對象真正的內存區域。

(之前寫過一個介紹對象的帖子,有興趣可以看看 傳送門

明白了這個原理之後,就可以將上面的例子形象化一下:

對象指向的內存區域相當於一個密碼箱,而對象本身man就是這個箱子的鑰匙。在執行updName(man)方法的時候,其實就相當於你複製了一把鑰匙,用來打開這個密碼箱。因爲打開的是同一個箱子,所以這個時候你把箱子裏面的水杯替換成筆記本之後,用原配的鑰匙(man)再打開箱子的時候,裏面的東西一定變成了筆記本,因爲你改變的是內存區域裏存儲的內容,而不是鑰匙。

那麼相對應的,如果你在複製而來的鑰匙上刻下你的名字,會影響原配鑰匙(man)嗎?當然不會,因爲這是兩個不同的鑰匙!

比如下面這個例子:

方法:

    /**
     * 對象重賦值
     * */
    public static void updObj(Man man){
        System.out.println("方法中重賦值前的名字:" + man.getName());
        System.out.println("方法中重賦值前的對象地址:" + man.toString());
        man = new Man();
        System.out.println("重新賦值後的名字:" + man.getName());
        System.out.println("重新賦值後的對象地址:" + man.toString());
    }

 

方法調用:

    man.setName("小明");
    System.out.println("調用方法之前的名字:" + man.getName());
    System.out.println("調用方法之前的對象地址:" + man.toString());
    updObj(man);
    System.out.println("調用方法之後的名字:" + man.getName());
    System.out.println("調用方法之後的對象地址:" + man.toString());

 

控制檯打印結果:

這裏就能很明顯的看出來,重賦值之後,對象指向的內存區域發生了改變(也就是換了一個箱子),不管你在方法中怎麼對重賦值後的對象操作,對原本對象以及對象中的內容是沒有任何改變的(往新箱子裏放東西不會影響舊箱子)。

也就是說方法中操作的其實是該對象的copy,並不是其本身。這就驗證了Java中是沒有引用傳遞的,只有值傳遞。

如果我寫的不夠簡單明瞭,請留評,我及時修改。

 

 

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