咱們知道,在Java中定義方法時,是能夠定義參數的,好比:java
public static void main(String[] args){ }
這裏的args就是一個字符串數組類型的參數。數組
在程序設計語言中,參數有形式參數和實際參數之分,先來看下它們的定義:ide
形式參數:是在定義函數名和函數體的時候使用的參數,目的是用來接收調用該函數時傳入的參數,簡稱「形參」。函數
實際參數:在主調函數中調用一個函數時,函數名後面括號中的參數稱爲「實際參數」,簡稱「實參」。this
舉個栗子:設計
public class ParamTest { public static void main(String[] args) { ParamTest pt = new ParamTest(); // 實際參數爲「張三」 pt.sout("張三"); } public void sout(String name) { // 形式參數爲 name System.out.print(name); } }
上面例子中,ParamTest類中定義了一個sout方法,該方法有個String類型的參數name,該參數即爲形參。在main方法中,調用了sout方法,傳入了一個參數「張三」,該參數即爲實參。指針
那麼,實參值是如何傳入方法的呢?這是由方法的參數傳遞機制來控制的。code
參數傳遞機制有兩種:值傳遞和引用傳遞。咱們先來看下程序語言中是如何定義和區分值傳遞和引用傳遞的:對象
值傳遞:是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中若是對參數進行修改,將不會影響到實際參數。內存
引用傳遞:是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。
那麼,在咱們大Java中,究竟是值傳遞仍是引用傳遞呢?
有了上面的概念,咱們就能夠一塊兒來探究一下,Java中方法參數究竟是值傳遞仍是引用傳遞了。
先看以下代碼:
public class ParamPass1 { public static void main(String[] args) { ParamPass1 p = new ParamPass1(); int i = 10; System.out.println("pass方法調用前,i的值爲=" + i); p.pass(i); System.out.println("pass方法調用後,i的值爲=" + i); } public void pass(int i) { i *= 3; System.out.println("pass方法中,i的值爲=" + i); } }
上面代碼中,咱們在類中定義了一個pass方法,方法內部將傳入的參數i的值增長至3倍,而後分別在pass方法和main方法中打印參數的值,輸出結果以下:
pass方法執行前,i的值爲=10 pass方法中,i的值爲=30 pass方法執行後,i的值爲=10
從上面運行結果來看,pass方法中,i的值是30,pass方法執行結束後,變量i的值依然是10。
能夠看出,main方法裏的變量i,並非pass方法裏的i,pass方法內部對i的值的修改並無改變實際參數i的值,改變的只是pass方法中i的值(pass方法中,i=30),由於pass方法中的i只是main方法中變量i的複製品。
所以同窗們很容易得出結論:Java中,一個方法不可能修改一個基本數據類型的參數 ,因此是值傳遞。
然而,結論下的還太早,由於方法參數共有兩種類型:
前面看到的只是基本數據類型的參數,那對於引用類型的參數,又是怎麼樣的呢?看以下代碼:
public class ParamPass2 { public static void main(String[] args) { ParamPass2 p = new ParamPass2(); User user = new User(); user.setName("張三"); user.setAge(18); System.out.println("pass方法調用前,user=" + user.toString()); p.pass(user); System.out.println("pass方法調用後,user=" + user.toString()); } public void pass(User user) { user.setName("李四"); System.out.println("pass方法中,user = " + user.toString()); } } class User { /** * 姓名 */ private String name; /** * 年齡 */ private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
上面代碼中,定義了一個User類,在main方法中,new了一個新的User對象user,而後給user對象的成員變量賦值,pass方法中,修改了傳入的user對象的屬性。
運行main方法,結果以下:
pass方法調用前,user= User{name='張三', age=18} pass方法中,user = User{name='李四', age=18} pass方法調用後,user= User{name='李四', age=18}
通過pass方法執行後,實參的值居然被改變了!!!那按照上面的引用傳遞的定義,實際參數的值被改變了,這不就是引用傳遞了麼?
有同窗可能會說:難道在Java的方法中,在傳遞基本數據類型的時候是值傳遞,在傳遞引用數據類型的時候是引用傳遞?
其實否則,Java中傳遞引用數據類型的時候也是值傳遞。
爲何呢?
先給你們說一下概念中的重點:
值傳遞,是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中若是對參數進行修改,將不會影響到實際參數。
引用傳遞,是指在調用函數時將實際參數的地址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。
總結下二者的區別:
值傳遞 | 引用傳遞 | |
---|---|---|
根本區別 | 會建立副本 | 不會建立副本 |
因此 | 函數中沒法改變原始對象 | 函數中能夠改變原始對象 |
敲黑板:複製的是參數的引用(地址值),並非引用指向的存在於堆內存中的實際對象。
main方法中的user是一個引用(也就是一個指針),它保存了User對象的地址值,當把user的值賦給pass方法的user形參後,即讓pass方法的user形參也保存了這個地址值,即也會引用到堆內存中的User對象。
上面代碼中,之因此產生引用傳遞的錯覺,是由於參數保存的是實際對象的地址值,你改變的只是地址值指向的堆內存中的實際對象,並無真正改變參數,參數的地址值沒有變。
下面結合生活中的場景,再來深刻理解一下值傳遞和引用傳遞。
你有一把鑰匙,當你的朋友想要去你家的時候,若是你直接把你的鑰匙給他了,這就是引用傳遞。這種狀況下,若是他對這把鑰匙作了什麼事情,好比他在鑰匙上刻下了本身名字,那麼這把鑰匙還給你的時候,你本身的鑰匙上也會多出他刻的名字。
你有一把鑰匙,當你的朋友想要去你家的時候,你復刻了一把新鑰匙給他,本身的還在本身手裏,這就是值傳遞。這種狀況下,他對這把鑰匙作什麼都不會影響你手裏的這把鑰匙。
可是,無論上面哪一種狀況,你的朋友拿着你給他的鑰匙,進到你的家裏,把你家的電視砸了。那你說你會不會受到影響?
咱們在pass方法中,改變user對象的name屬性的值的時候,不就是在「砸電視」麼。你改變的不是那把鑰匙(地址值),而是鑰匙打開的房子(地址值對應的實際對象)。
那咱們如何真正的改變參數呢,看以下代碼:
public class ParamPass3 { public static void main(String[] args) { ParamPass3 p = new ParamPass3(); User user = new User(); user.setName("張三"); user.setAge(18); System.out.println("pass方法調用前,user= " + user.toString()); p.pass(user); System.out.println("pass方法調用後,user= " + user.toString()); } public void pass(User user) { user = new User(); user.setName("李四"); user.setAge(20); System.out.println("pass方法中,user = " + user.toString()); } } class User { /** * 姓名 */ private String name; /** * 年齡 */ private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
在這段代碼中,pass方法中,咱們真正的改變了user參數,由於它指向了一個新的地址(user = new User()),即參數的地址值改變了。運行結果以下:
pass方法調用前,user= User{name='張三', age=18} pass方法中,user = User{name='李四', age=20} pass方法調用後,user= User{name='張三', age=18}
從結果看出,對參數進行了修改,沒有影響到實際參數。
因此說,Java中其實仍是值傳遞的,只不過對於引用類型參數,值的內容是對象的引用。
更多好文請關注公衆號