直接上例子
1.方法中,局部變量爲基本類型
public class BaseChange {
static void change(int a) {
a += 5;
}
public static void main(String[] args) {
int a = 1;
change(a);
System.out.println(a);
}
}
得到的結果是 1。
2.方法中,局部變量爲數組
static void changeArr(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] += 100;
}
}
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 };
changeArr(arr);
System.out.println(Arrays.toString(arr));
}
得到的結果是:
[101, 102, 103, 104]
3.方法中,局部變量爲String
private static void change(String s) {
s += "123";
}
public static void main(String[] args) {
String s = "abc";
change(s);
System.out.println(s);
}
得到的結果是:
abc
4.方法中,爲自定義對象
Foo
public class Foo {
private int a;
private int b;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
@Override
public String toString() {
return "Foo [a=" + a + ", b=" + b + "]";
}
}
測試類:
static void changeFoo(Foo foo) {
foo.setA(100);
foo.setB(200);
}
public static void main(String[] args) {
System.out.println("--change object-");
Foo foo = new Foo();
System.out.println(foo);
changeFoo(foo);
System.out.println(foo);
}
得到的結果是:
--change object-
Foo [a=0, b=0]
Foo [a=100, b=200]
通過上面4個例子,可以得出以下結論:
1.當方法中的參數爲int等基本類型時候,方法結束,值不變;
2.當方法中參數爲String的時候,方法結束,值也不變;
3.當方法中參數爲對象(自定義對象和數組),方法技術,值發生了變化。
方法以棧幀的形式被虛擬機加載到虛擬棧裏面,每個線程單獨的管理自己的虛擬棧,所以,多個線程調用同一個方法,是相互不干擾的(前提不對全局的數據修改);
方法結束的時候,棧幀退出虛擬機棧。此時,所有的方法中的入參的聲明週期就結束了。
然後來回顧上面4段代碼:
第一段代碼:
1.當main中調用 int a=1的時候,基本變量a入虛擬棧
2.當調用方法change的時候,change方法棧幀入棧,局部變量a在棧幀上操作a+=5
3.當change方法結束的時候,棧幀退棧,打印a的值,a仍然是1。
因爲java中,基本類型就是存儲在虛擬棧中的。當然如果是全局變量,或者靜態變量,至少可以肯定的是,不是存儲在線程私有的虛擬棧上面的。
第二段代碼和第四段代碼
把他們放在一起講,是筆者粗糙的認爲,數組也是對象,所以直接放在一起解釋。對於對象,虛擬機棧中存儲的是該對象的應用,對象在堆上分配內存。
1.首先,方法進入前,變量引用入虛擬機棧,實際內容入堆,引用指向內容,且內容被初始化x:(當然,虛擬機實際的存儲模型裏面,還有一個類的信息,但是,數據確實是在堆裏修改的)
2.當調用change方法的時候,引用作爲參數傳輸到棧幀中,這時,兩個引用同時都指向對象,然後,方法中進行操作,綠色框框中的引用將對象的值改爲M
3.方法結束,棧幀退棧
因此,最後打印的結果,發生了變化,感覺是方法裏面把值修改了。
再來看第三段代碼:
我們先假設,String的值分配在堆上,但實際上(String有可能被分配在方法代,書上說的)。
String是一種特殊的對象,在堆上的表現是不可動態擴展的,就是如果 a="1"執行了a+="2"的操作,那麼,堆上會產生兩個String對象,一個是"1",一個是"2"。(java在編譯的過程中,高版本的或許會將a="1",a+="2"的這種代碼進行優化,不過,並不妨礙我們分析String作爲參數傳遞到方法中).
ok,現在來進行分析代碼:
1.首先String s="abc",此時,虛擬棧上會有一個s的變量引用,堆上有一個“abc”的對象,s的變量引用指向堆上的"abc"
2.調用change方法,棧幀入棧,參數棧,藍色的引用作爲參數傳遞給綠色的引用,這是,他們指向同一個對象:
3.方法中,執行s+="123",前面有說到,String比較特殊,他不會像其他對象一樣,在內部進行修改,而是新分配一塊內存。這是可以看到,方法中的引用和藍色的引用,已經指向的不是同一個對象了。
4.方法執行完畢,棧幀退棧:
所以,最終輸出的結果,仍然是"abc"。