《Java從入門到失業》第四章:類和對象(4.4):方法參數及傳遞


4.4方法參數及傳遞

       關於這個知識點,我想了很久該不該在這里闡述。因為這個知識點稍微有點晦澀,並且就算不了解也不影響用Java編寫代碼。不過筆者剛開始工作的時候,就是因為這塊內容沒有過多的關注,以至於相當於長一段時間對這塊內容都模糊不清甚至誤解。我相信你們都比我悟性高,因此決定還是先拿出來討論。

       我們知道,一個方法一般由修飾符、返回值、方法名和參數列表構成。這里我們主要討論方法的參數。看一個例子:

  // 構造方法  
    public Mahjong(int type, int number) {  
        this.type = type;  
        this.number = number;  
    }  

這是麻將類的構造方法,有2個參數。我們看到參數由參數類型和參數名構成。參數類型可以是任何類型(即基本數據類型、類類型)。參數名需要滿足標識符規范,一般建議使用有含義的名稱。因為方法將會作為API的一部分暴露給調用者閱讀,不要因為參數名的晦澀難懂而影響可讀性。

4.4.1形參和實參

       我們看一下構造一個麻將的代碼:

int t= 1;  
int n = 2;  
Mahjong m = new Mahjong(t, n); 

形參:上面麻將構造方法中的參數type、number,我們稱之為形參,即形式參數。形參是定義方法的時候使用的參數,用來接收調用者傳遞的參數。方法在調用的時候,形參才會被分配內存空間,一旦方法調用完畢,形參的內存就會被釋放。

實參:這段代碼中,我們先定義2個參數t和n,然后把t和n傳遞給麻將類的構造方法,t和n我們稱之為實參,即實際參數。實參是調用者傳遞給方法的參數,實參需要在調用之前賦值,即在方法調用之前就已經分配了內存空間,並且方法調用完畢之后內存不會釋放。用一張圖來示意:

 

4.4.2值調用和引用調用

       從上一小節我們看到,當調用方法的時候傳遞的是基本數據類型時,實際上是把實參的內存中的值傳遞給形參,這種方法調用我們稱之為“值調用”。

實際上,在程序語言中還有一種稱作“引用調用”的方式,例如C++同時存在值調用和引用調用兩種方式。引用調用是把實參內存地址傳遞給形參。注意和值調用的區別:

  • 值調用傳遞的是實參“內存的值”
  • 引用調用傳遞的是實參“內存的地址”

可能有的同學有點懵了,內存的值和內存的地址有什么區別?回憶一下我們在第一章介紹內存的時候用來作比喻的蜂巢,蜂巢的每一個格子就相當於內存,它們都有一個唯一的編號,這就是內存地址,而格子里存放的東西就是內存的值。只不過內存的地址和內存的值都是二進制,因此容易混淆。

       事實上,在Java語言中,只有值調用一種方式,不管傳遞的是基本數據類型還是類類型。值調用因為傳遞的是內存的值,因此不管傳遞的是基本數據類型還是類類型,都不會改變實參內存中的值。我們先看一個基本數據類型的例子:

public class Method {  
    public static void changeValue(int value) {  
        value += 4;  
    }  
  
    public static void main(String[] args) {  
        int v = 5;  
        changeValue(v);  
        System.out.println(v);// 輸出結果是5,v的值沒有改變  
    }  
}  

我們看到,定義int變量v,然后傳遞給changeValue方法,方法內部把形參的值加4,但是對於實參v的值,並沒有發生變化。為什么呢?實際上這個執行的過程如下:

  1. 定義變量v,給v分配一塊內存,內存中的值存放5
  2. 調用changeValue方法,分配一塊內存給形參value,並將v的值拷貝到value的內存中
  3. 執行方法,將value內存中的值加4,變成9,但是v的內存存放仍然是5
  4. 方法結束,釋放value內存

我們用一張圖來解釋:

 

我們再看一個傳遞類類型方法調用的代碼:

我們先給美人類增加一個修改器方法:

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

然后,寫一個changeName方法,並調用,代碼如下:

public class Method {  
  
    public static void changeName(Player player) {  
        player.setName("西施");  
    }  
  
    public static void main(String[] args) {  
        Player diaochan = new Player("貂蟬");  
        changeName(diaochan);  
        System.out.println(diaochan.getName());// 結果輸出 西施  
    }  
} 

前面我們說過值調用不會改變形參的值,但是這里好像把貂蟬的名字改成西施了,為啥呢?我們先分析下執行過程:

  1. 定義變量diaochan並構造一個美人對象賦值給它,給diaochan分配一塊內存,同時在堆內存中分配空間存放美人對象。變量diaochan內存中的存放的是美人對象的地址,假設地址為0xA1
  2. 調用changeName方法,分配一塊內存給形參player,並將diaochan的值拷貝到player的內存中,因此形參player的值也為0xA1,指向美人對象
  3. 執行changeName方法,調用形參player的修改器setName方法,實際上就是調用美人對象的setName方法,因此美人對象的名字變成“西施”。
  4. 方法結束,釋放形參player內存,實參diaochan和美人對象的內存依然存在

我們也用一張圖來演示:

 

我們看到自始至終,實參diaochan內存中的值一直沒變,都是0xA1。因為美人對象的名字變了,因此有的網文甚至有的書籍說Java類類型是引用調用,筆者認為是屬於錯誤的說法。因為看是否是值調用,根本是要看是否傳遞的是實參內存的值,Java中類類型的傳遞,也是傳遞的實參內存中的值,只不過這個值是一個對象的地址(即引用)。


免責聲明!

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



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