值傳遞
在方法被調用時,實參通過形參把它的內容副本傳入方法內部,此時形參接收到的內容是實參值的一個拷貝,因此在方法內對形參的任何操作,都僅僅是對這個副本的操作,不影響原始值的內容。
先來看個例子:
public static void valueCross(int age,float weight) { System.out.println("傳入的age值:"+age); System.out.println("傳入的weight值:"+weight); age=23; weight=60; System.out.println("修改后的age值:"+age); System.out.println("修改后的weight值:"+weight); } public static void main(String[] args) { int age=10; int weight=50; valueCross(age,weight); System.out.println("方法執行后的age:"+age); System.out.println("方法執行后的weight:"+weight); }
運行結果:
傳入的age值:10 傳入的weight值:50.0 修改后的age值:23 修改后的weight值:60.0 方法執行后的age:10 方法執行后的weight:50
我們可以看到valueCross方法執行后,實參age和weight的值並沒有發生變化,這是什么原因?
首先程序運行時,先從main方法開始執行,此時JVM為main()方法往虛擬機棧中壓入一個棧幀,即為當前棧幀,用來存放main()中的局部變量表(包括參數)、操作棧、方法出口等信息,如a和w都是mian()方法中的局部變量,因此可以斷定,age和weight是躺着mian方法所在的棧幀中
接着調用valueCross方法,此時JVM為valueCross()方法往虛擬機中壓入一個棧幀,即為當前棧幀,用於存放valueCross方法的局部變量等信息;因此age和weight是躺着valueCrossTest方法所在的棧幀中,而他們的值是從a和w的值copy了一份副本而得,如圖:
因此這兩個age和weight對應的內容不是同一個,在valueCross方法中修改的只是自己棧中的內容,並沒有修改main方法棧中的內容
引用傳遞
”引用”也就是指向真實內容的地址值,在方法調用時,實參的地址通過方法調用被傳遞給相應的形參,在方法體內,形參和實參指向同一塊內存地址,對形參的操作會影響的真實內容。
先有一個Person類代碼如下:
public class Person { 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; } }
測試類代碼:
public class TestArr { public static void PersonCross(Person person) { System.out.println("傳入的person的name:"+person.getName()); person.setName("敖丙"); System.out.println("方法內重新賦值后的name:"+person.getName()); } public static void main(String[] args) { Person p=new Person(); p.setName("哪吒"); p.setAge(4); PersonCross(p); System.out.println("方法執行后的name:"+p.getName()); } }
測試結果1:
傳入的person的name:哪吒 方法內重新賦值后的name:敖丙 方法執行后的name:敖丙
我們可以看到PersonCross方法執行后,person的name值被改變了
下面再看一段代碼:
public class TestArr { public static void PersonCross(Person person) { System.out.println("傳入的person的name:"+person.getName()); person=new Person();//新添加的代碼 person.setName("敖丙"); System.out.println("方法內重新賦值后的name:"+person.getName()); } public static void main(String[] args) { Person p=new Person(); p.setName("哪吒"); p.setAge(4); PersonCross(p); System.out.println("方法執行后的name:"+p.getName()); } }
測試結果2:
傳入的person的name:哪吒 方法內重新賦值后的name:敖丙 方法執行后的name:哪吒
有沒有發現什么不同,這次PersonCross方法執行后person的name值並沒有改變,這又是為什么?
我們知道,java中的對象和數組是存放在堆內存中的,而堆內存是線程共享的,所以main方法執行時,會在堆內存中開辟一塊內存,用來存儲p對象的所有內容,然后再棧內存中創建一個引用p存儲堆區中p對象的真實地址,如下圖:
當執行到PersonCross方法時,因為方法內有這么一行代碼:person=new Person(),此時JVM在堆內存中又開辟了一塊內存空間,假設地址為xo2222,那么現在的person則指向了xo2222這塊內存,現在修改person的name值修改的是xo2222這塊內存空間的值,不會改變xo3333的值,所以測試結果2中的name沒有發生變化
引用傳遞本質上就是值傳遞,將引用變量的值傳遞給形參,因為引用變量的值存放的是地址值,所以當地址值傳遞給形參后,形參和實參指向同一塊內存區域。