Java的call by value_call by value or reference ?
Java中參數(shù)傳遞是傳值還是傳引用呢?很多人遇到這個(gè)問(wèn)題都會(huì)馬上給你拋出這個(gè)例子:
對(duì)方不想和你說(shuō)話并向你拋了一段代碼
class Entry{
Integer value;
public Entry(Integer v){
this.value = v;
}
@Override
public String toString() {
return "Entry[value=" + value + "]";
}
}
public class CallByDemo{
public static void swap(int a,int b){
int temp = a;
a = b;
b = a;
}
public static void swap(Entry e1,Entry e2){
Integer temp = e1.value;
e1.value = e2.value;
e2.value = temp;
}
public static void main(String[] args) {
int a = 1;
int b = 2;
System.out.println("before:a="+a+",b="+b);
swap(a,b);
System.out.println("after :a="+a+",b="+b);
Entry e1 = new Entry(new Integer(1000));
Entry e2 = new Entry(new Integer(2000));
System.out.println("before:e1="+e1+"e2="+e2);
swap(e1,e2);
System.out.println("after :e1="+e1+"e2="+e2);
}
}
運(yùn)行結(jié)果:
before:a=1,b=2
after :a=1,b=2
before:e1=Entry[value=1000]e2=Entry[value=2000]
after : e1=Entry [value=2000 ]e2=Entry[value=1000]
然后言之鑿鑿地拋出這個(gè)結(jié)論:
當(dāng)參數(shù)為基本類型時(shí)為傳值
當(dāng)參數(shù)為對(duì)象引用類型為傳引用
好像沒(méi)有毛病啊,但是如果我把swap(Entry e1,Entry e2)改成這樣呢?
public static void swap(Entry e1,Entry e2){
Entry temp = e1;//Integer temp = e1.value;
e1 = e2; //e1.value = e2.value;
e2 = temp;//e2.value = temp;
}
再次運(yùn)行發(fā)現(xiàn)結(jié)果變成了這樣:
before:a=1,b=2
after :a=1,b=2
before:e1=Entry[value=1000]e2=Entry[value=2000]
after : e1=Entry [value=1000 ]e2=Entry[value=2000]
什么?怎么會(huì)這樣?
為了解釋這個(gè)問(wèn)題,我們不妨看一下Java運(yùn)行時(shí)內(nèi)存結(jié)構(gòu):
Java Run-Time Data Areas
Java堆 (Java Heap)
作用:存放幾乎所有的對(duì)象實(shí)例和數(shù)組
組成
新生代(Young Generation)
Eden區(qū):存放新創(chuàng)建的對(duì)象或短期的對(duì)象
Survivor區(qū):存放GC后的幸存的或中期的對(duì)象
老年代(Old Generation):存放GC多次后始終存在或者長(zhǎng)期的對(duì)象及Survivor區(qū)放不下的大對(duì)象
永久代(Permanent Generation):永久代在JDK8中被完全地移除
是否線程共享:是
Java虛擬機(jī)棧(JVM Stacks)
作用:存放棧幀
組成:棧幀
是否線程共享:線程私有的,生命周期和線程的相同
棧幀(Stack Frame)
- 作用:方法在執(zhí)行的時(shí)候,都會(huì)有一個(gè)棧幀創(chuàng)建出來(lái),用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息
- 組成:
- 局部變量表(Local Variables):存放編譯時(shí)可知的各種基本數(shù)據(jù)類型、對(duì)象引用
- 操作數(shù)棧(Operand Stacks):供方法調(diào)用時(shí)進(jìn)行各種運(yùn)算
- 動(dòng)態(tài)鏈(Dynamic Linking):
- 方法出口
方法區(qū)(Method Area)
作用:存放被虛擬機(jī)加載的類的結(jié)構(gòu)信息(如:字段和方法數(shù)據(jù)、方法的字節(jié)碼、運(yùn)行時(shí)常量池等)、常量,靜態(tài)變量及類、實(shí)例、接口初始化時(shí)用到的特殊方法。
組成:方法區(qū)是堆的邏輯組成部分(有人稱之為永久代 Permanent Generation)
是否線程共享:是
本地方法棧(Native Method Stacks)
作用:存放本地方法調(diào)用時(shí)的棧幀
組成:棧幀
是否線程共享:線程私有的,生命周期和線程的相同
虛擬機(jī)執(zhí)行Native方法時(shí)使用,不同的虛擬機(jī)有不同的實(shí)現(xiàn)方法,HotSpot虛擬機(jī)的本地方法棧和虛擬機(jī)棧合二為一。
PC寄存器/程序計(jì)數(shù)器(pc Register)
作用:保存JVM正在執(zhí)行方法的字節(jié)碼指令的地址,如果該方法為native本地方法則為undefined
組成:一塊至少能夠保存一個(gè)本地指針或者returnAddress的值的內(nèi)存空間
是否線程共享:每個(gè)線程都有一個(gè)獨(dú)立的程序計(jì)數(shù)器,各個(gè)線程之間計(jì)數(shù)器互不影響,獨(dú)立存儲(chǔ)
OK,我們?cè)賮?lái)分析一下上面的問(wèn)題:
內(nèi)存結(jié)果分析
其實(shí)呢,Java采用的是傳值(call by value),形參只是實(shí)際參數(shù)的一個(gè)拷貝,形參不能修改實(shí)參的內(nèi)容。
當(dāng)值為基本數(shù)據(jù)類型時(shí),swap(int,int)方法中的局部變量a,b接收傳入的值并保存在與該方法對(duì)應(yīng)的棧幀的局部變量表中。而main方法中的a,b保存在main方法對(duì)應(yīng)的棧幀的局部變量表中,修改swap方法中的a,b對(duì)main方法中的a,b沒(méi)有任何影響,所以交換失敗。
當(dāng)值為引用類型時(shí),傳入方法的也是它的一個(gè)拷貝,當(dāng)然這個(gè)拷貝有點(diǎn)特殊,它是Java Heap中的對(duì)象(Entry_e1、Entry_e2)的一個(gè)引用。該引用也保存在對(duì)應(yīng)的棧幀的局部變量表中,修改swap方法中的e1,e2的引用指向?qū)ain方法中的e1,e2沒(méi)有任何影響,所以交換失敗。但局部變量e1,e2可以通過(guò)引用改變Heap中的對(duì)象的狀態(tài),如第一段代碼中在swap中的局部變量可以通過(guò)引用來(lái)修改Heap中的對(duì)象的value屬性,從而達(dá)到交換屬性中的目的。
此外,需要注意的是Java中的某些類如:String、基本類型的包裝類、BigInteger、BigDecimal是不可變的,即無(wú)法修改其內(nèi)容。
最后總結(jié)一句:Java是方法調(diào)用是值傳遞!
總結(jié)
以上是生活随笔為你收集整理的Java的call by value_call by value or reference ?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 腻子粉那个牌子的好?
- 下一篇: java中override快捷键_【基础