java是值傳遞;值傳遞是指在呼叫方法時將實際參數拷貝一份傳遞到方法中,這樣在方法中如果對參數進行修改,將不會影響到實際參數;當傳的是基本型別時,傳的是值的拷貝,對拷貝變數的修改不影響原變數;當傳的是引用型別時,傳的是引用位址的拷貝,但是拷貝的位址和真實位址指向的都是同一個真實數據,因此可以修改原變數中的值。
本教學操作環境:windows10系統、DELL G3電腦。
Java是值傳遞。
當傳的是基本型別時,傳的是值的拷貝,對拷貝變數的修改不影響原變數;當傳的是引用型別時,傳的是引用位址的拷貝,但是拷貝的位址和真實位址指向的都是同一個真實數據,因此可以修改原變數中的值;當傳的是String類型時,雖然拷貝的也是引用位址,指向的是同一個數據,但是String的值不能被修改,因此無法修改原變數中的值。
首先來解釋什麼是引用傳遞,什麼是值傳遞。
引用傳遞(pass by reference)是指在呼叫方法時將實際參數的位址直接傳遞到方法中,那麼在方法中對參數所進行的修改,將會影響到實際參數。
值傳遞(pass by value)是指在呼叫方法時將實際參數拷貝一份傳遞到方法中,這樣在方法中如果對參數進行修改,將不會影響到實際參數。
那在Java中到底是引用傳遞還是值傳遞呢?其實這個問題也一直是爭論不斷,而且官方也沒給個確切答案。但就我個人理解,Java是值轉遞。
我們先來看一個簡單的例子:
public void test() { int a = 1; change(a); System.out.println("a的值:" + a); } private void change(int a) { a = a + 1; } // 输出 a的值:1
在test()方法中定義了一個基本類型的變數a,然後呼叫change()方法試圖改變這個變量,最後輸出的還是原來的值。
首先我們要清楚,一個方法中的局部變數是存在堆疊中的,如果是基本型別的變數則直接存的是這個變數的值,如果是引用型別的變數則存的是值的位址,指向堆中具體的值。
上面的範例中,呼叫change()方法傳遞的a,其實是a變數的拷貝,不是真正的a,在change()方法中改變的是拷貝,對真正的a是沒有影響的。
這麼一看,Java確實是值傳遞,但我們再看下面這個例子,你就會糾結了
public void test() { User user = new User(); user.setAge(18); change(user); System.out.println("年龄:" + user.getAge()); } private void change(User user) { user.setAge(19); } // 输出 年龄:19
看,物件裡的屬性被改變了,不是值傳遞嗎,應該不會改變啊,這時候就有人總結了,當傳遞值是基本型別時是值傳遞、當傳的是引用型別時是引用傳遞。真的是這樣嗎?
分析這個問題,我們需要知道變數在jvm中是怎麼儲存的。
首先看基本類型,這個很簡單,變數在堆疊中直接存的是值,傳到change()方法的是這個變數的拷貝,因此對拷貝的變數修改不會影響原變數的值。
接著看引用類型,變數在堆疊中儲存的是引用位址,這個位址指向堆中具體的值,如下圖:
當調用change()方法傳入變數時,也是拷貝變量,但是這裡的拷貝只是堆疊中的引用位址,並不會拷貝堆中的數據,因此會變成下圖這樣:
雖然變數是拷貝,但是指向的位址是同一個,因此對變數中的資料修改時,還是會影響到原來真實的變量,但是,如果我們修改的是變數在堆疊中的位址,則不會影響原變量,例如下面這段程式碼:
public void test() { User user = new User(); user.setAge(18); change(user); System.out.println("年龄:" + user.getAge()); } private void change(User user) { user = new User(); user.setAge(19); } // 输出 年龄:18
這種是修改變數在堆疊中的位址,則不會影響原變數。
說到這裡,大家差不多懂了,但是回頭看最開始的那個問題,傳入String類型的變量,String是引用類型,按道理,原變量是會被改變的呀,結果怎麼是不變呢?
String變數比較特殊,我們看String的源碼可以知道,String的值是透過內部的char[]陣列來維護的,但是這個資料定義的是final型別的,因此,String的值是不可變的。我們平常會修改String的值,其實是重新new了一個String對象,例如下面這段程式碼:
String a = "hello"; a = "world";
這段程式碼裡,其實a變數並沒有被修改成world,只是重新new了一個String對象,這個對象的值是world,並把這個對象的引用地址賦給了a,原來的hello還是在堆中,只是這個值沒有被引用,過段時間會被gc垃圾回收。
String變數傳值在記憶體中的變化如下圖:
String拷貝的是變數位址,但是它改變不了原String的值,因為String是不可變的,所以在change()方法中是重新new了一個String對象,改變的是新物件的值,原變數是沒有影響的。
更多相關知識,請造訪常見問題欄位!
以上是java是值傳遞還是引用傳遞的詳細內容。更多資訊請關注PHP中文網其他相關文章!