Java 編程語言中最大的困惑之一就是: java 是按值傳遞仍是按引用傳遞。我在面試中常常會問面試者這個問題,但仍是有不少面試者對這個問題的理解不是很正確。
有不少面試者是這樣理解的:java
這樣的理解正確嗎?他們甚至還能夠寫出示例代碼來驗證他們的想法,讓咱們來一塊兒看一看大多數人是如何驗證「基礎類型按值傳遞,非基礎類型按引用傳遞」這個想法的:程序員
基礎類型數據做爲參數傳遞面試
/** * 基礎類型數據做爲參數傳遞 * @Author: danding * @Date: 2019/11/5 */ public class TestParams { public static void main(String[] args){ int x = 6; System.out.println("x的初始值爲:" + x); add(x); System.out.println("x的最終值爲:" + x); } public static void add(int x){ x = x + 1; System.out.println("add 方法中的x值爲:" + x); } }
運行結果:算法
x的初始值爲:6 add 方法中的x值爲:7 x的最終值爲:6
非基礎類型做爲參數傳遞
首先咱們定義一個類編程
/** * 定義一個女友的類 * (簡陋了點,只有年齡,但不影響咱們使用呀) * @Author: danding * @Date: 2019/11/5 */ public class GrilFriend { private int age; public GrilFriend(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
而後咱們將建立實例並做爲參數傳遞編程語言
/** * 基礎類型數據做爲參數傳遞 * @Author: danding * @Date: 2019/11/5 */ public class TestParams { public static void main(String[] args){ GrilFriend gril = new GrilFriend(18); System.out.println("女友的初始年齡爲:" + gril.getAge()); add(gril); System.out.println("女友的最長年齡爲:" + gril.getAge()); } private static void add(GrilFriend friend){ friend.setAge(friend.getAge()+1); System.out.println("女友在方法中的年齡爲:" + friend.getAge()); } }
運行結果:this
女友的初始年齡爲:18 女友在方法中的年齡爲:19 女友的最長年齡爲:19
非基礎類型做爲參數傳遞時,值的確被修改了。code
這個時候不少同窗經過以上兩個示例驗證,本身就得出了本身的結論:視頻
若是傳遞類型爲基礎數據類型,則按值傳遞,不然爲按引用傳遞。對象
在此說明,這個理解是錯誤的,錯誤的,錯誤的。下面咱們就來講說 Java中的參數傳遞究竟是按值傳遞仍是按引用傳遞?
首先說下正確的答案:Java 的參數傳遞,不論是基本數據類型仍是引用類型的參數,都是按值傳遞,沒有按引用傳遞!
首先,咱們應該瞭解按值傳遞或按引用傳遞的含義。
你個糟老頭子壞得狠,我信你個鬼,你這個解釋給我要給差評.....
且聽老夫(哦,不,是小編)慢慢道來...
當一個對象被看成參數傳遞到一個方法後,在此方法內能夠改變這個對象的屬性,那麼這裏究竟是「按值傳遞」仍是「按引用傳遞」?
答:是按值傳遞。Java 語言的參數傳遞只有「按值傳遞」。當一個實例對象做爲參數被傳遞到方法中時,參數的值就是該對象的引用的一個副本。指向同一個對象,對象的內容能夠在被調用的方法內改變,但對象的引用(不是引用的副本) 是永遠不會改變的。
這個上面的示例已經驗證了,爲按值傳遞,這個你們應該不會有什麼異議。
咱們重點來講下對象類型做爲參數傳遞
先來看一下傳遞的例子:
public class TestParams { public static void main(String[] args){ Person p1 = new Person(); System.out.println(p1); change(p1); System.out.println(p1); } private static void change(Person p2){ p2 = new Person(); } } class Person{ }
運行結果
Person@677327b6 Person@677327b6
能夠看出兩次打印person的地址值是同樣的,即調用完change() 方法以後,person變量並無發生改變。
這個傳遞過程的示意圖以下:
當執行到Person p1 = new Person();代碼時,程序在堆內存中開闢了一塊內存空間用來存儲Person類的實例對象,同時在棧內存中開闢了一個存儲單元用來存儲該實例對象的引用,即上圖中person指向的存儲單元。
當執行到change(p1);代碼時,person做爲參數傳遞給change()方法,須要注意的是:person將本身存儲單元的內容傳遞給了change()方法的p2變量!此後,在change()方法中對p2的一切操做都是針對p2所指向的存儲單元,與person所指向的那個存儲單元沒有關係了!
這個時候該有同窗說了,那上面那個女友示例中,女友的年齡不是被在方法中修改了嗎?若是傳遞的是副本那不該該修改不了女友的年齡嗎?
若是咱們將女友中的代碼放到內存示例圖中走一遍,你應該就明白其中的道理了。
所謂引用副本,但其所指向的仍是真實的對象,因此修改的仍是真實對象上的屬性。
我但願上面的解釋能消除全部疑問,只須要記住Java 的參數傳遞,不論是基本數據類型仍是引用類型的參數,都是按值傳遞,沒有按引用傳遞!。當您將瞭解堆空間和棧內存以及存儲不一樣對象和引用的位置時,將會更加清楚,有關程序的詳細說明,請閱讀 Java Heap vs Stack。
「不積跬步,無以致千里」,但願將來的你能:有夢爲馬 隨處可棲!加油,少年!
關注公衆號:「Java 知己」,天天更新Java知識哦,期待你的到來!