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的值,並沒有發生變化。為什么呢?實際上這個執行的過程如下:
- 定義變量v,給v分配一塊內存,內存中的值存放5
- 調用changeValue方法,分配一塊內存給形參value,並將v的值拷貝到value的內存中
- 執行方法,將value內存中的值加4,變成9,但是v的內存存放仍然是5
- 方法結束,釋放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());// 結果輸出 西施 } }
前面我們說過值調用不會改變形參的值,但是這里好像把貂蟬的名字改成西施了,為啥呢?我們先分析下執行過程:
- 定義變量diaochan並構造一個美人對象賦值給它,給diaochan分配一塊內存,同時在堆內存中分配空間存放美人對象。變量diaochan內存中的存放的是美人對象的地址,假設地址為0xA1
- 調用changeName方法,分配一塊內存給形參player,並將diaochan的值拷貝到player的內存中,因此形參player的值也為0xA1,指向美人對象
- 執行changeName方法,調用形參player的修改器setName方法,實際上就是調用美人對象的setName方法,因此美人對象的名字變成“西施”。
- 方法結束,釋放形參player內存,實參diaochan和美人對象的內存依然存在
我們也用一張圖來演示:
我們看到自始至終,實參diaochan內存中的值一直沒變,都是0xA1。因為美人對象的名字變了,因此有的網文甚至有的書籍說Java類類型是引用調用,筆者認為是屬於錯誤的說法。因為看是否是值調用,根本是要看是否傳遞的是實參內存的值,Java中類類型的傳遞,也是傳遞的實參內存中的值,只不過這個值是一個對象的地址(即引用)。