題解 「BZOJ2137」submultiple
值傳遞
在方法被呼叫時,實參通過形參把它的內容副本傳入方法內部,此時形參接收到的內容是實參值的一個拷貝,因此在方法內對形參的任何操作,都僅僅是對這個副本的操作,不影響原始值的內容。
先來看個例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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);
}
|
執行結果:
1 2 3 4 5 6 |
傳入的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類程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
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;
}
}
|
測試類程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
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:
1 2 3 |
傳入的person的name:哪吒
方法內重新賦值後的name:敖丙
方法執行後的name:敖丙
|
我們可以看到PersonCross方法執行後,person的name值被改變了
下面再看一段程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
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:
1 2 3 |
傳入的person的name:哪吒
方法內重新賦值後的name:敖丙
方法執行後的name:哪吒
|
有沒有發現什麼不同,這次PersonCross方法執行後person的name值並沒有改變,這又是為什麼?
我們知道,java中的物件和陣列是存放在堆記憶體中的,而堆記憶體是執行緒共享的,所以main方法執行時,會在堆記憶體中開闢一塊記憶體,用來儲存p物件的所有內容,然後再棧記憶體中建立一個引用p儲存堆區中p物件的真實地址,如下圖:
當執行到PersonCross方法時,因為方法內有這麼一行程式碼:person=newPerson(),此時JVM在堆記憶體中又開闢了一塊記憶體空間,假設地址為xo2222,那麼現在的person則指向了xo2222這塊記憶體,現在修改person的name值修改的是xo2222這塊記憶體空間的值,不會改變xo3333的值,所以測試結果2中的name沒有發生變化
引用傳遞本質上就是值傳遞,將引用變數的值傳遞給形參,因為引用變數的值存放的是地址值,所以當地址值傳遞給形參後,形參和實參指向同一塊記憶體區域。