String是值傳遞還是引用傳遞
今天上班時奴拦,同事發(fā)現(xiàn)了一個比較有意思的問題。他把一個String類型的參數(shù)傳入方法届吁,并在方法內(nèi)改變了引用的值错妖。
然后他在方法外使用這個值,發(fā)現(xiàn)這個String還是之前的值疚沐,并沒有改變暂氯。
這里要向大家介紹一下,大家都知道java在傳參時分為值 傳遞 和 引用傳遞 亮蛔。參數(shù)為基本類型時是值傳遞痴施,
參數(shù)為封裝類型時是引用傳遞。例如:
基本類型參數(shù)
public class Test {
public static void main(String[] args) {
int num = 0 ;
changeNum(num);
System.out.println("num="+num);
}
private static void changeNum(int num) {
num = 1;
}
}
打印的結(jié)果是num=0
究流。
封裝類型參數(shù)
public class Test {
public static void main(String[] args) {
Product p = new Product();
p.setProName("before");
p.setNum(0);
changeProduct(p);
System.out.println("p.proName="+p.getProName());
System.out.println("p.num="+p.getNum());
}
private static void changeProduct(Product p) {
p.setProName("after");
p.setNum(1);
}
}
class Product {
private int num;
private String proName;
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getProName() {
return proName;
}
public void setProName(String proName) {
this.proName = proName;
}
}
運行的結(jié)果是:p.proName=after
和p.num=1
辣吃。
上面的兩個例子是明顯的值傳遞和引用傳遞。但是如果參數(shù)是String類型呢芬探?我們看一下具體的例子:
public class Test {
public static void main(String[] args) {
String str = "ab";
changeString(str);
System.out.println("str="+str);
}
private static void changeString(String str) {
str = "cd";
}
}
大家猜一下運行結(jié)果是什么呢神得?按照前面的例子,String應(yīng)該是一個封裝類型灯节,它應(yīng)該是引用傳遞循头,是可以改變值得绵估,
運行的結(jié)果應(yīng)該是"cd"。我們實際運行一下看看卡骂,
str=ab
,這如何解釋呢国裳?難道String是基本類型?也說不通呀全跨。
這就要從java底層的機制講起了缝左,java的內(nèi)存模型分為 堆 和 棧 。
1.基本類型的變量放在棧里浓若;
2.封裝類型中渺杉,對象放在堆里,對象的引用放在棧里挪钓。
java在方法傳遞參數(shù)時是越,是將變量復(fù)制一份,然后傳入方法體去執(zhí)行碌上。 這句話是很難理解的倚评,也是解釋這個
問題的精髓。我們先按照這句話解釋一下基本類型的傳遞
- 虛擬機分配給num一個內(nèi)存地址馏予,并且存了一個值0.
- 虛擬機復(fù)制了一個num天梧,我們叫他num',num'和num的內(nèi)存地址不同霞丧,但存的值都是0呢岗。
- 虛擬機講num'傳入方法,方法將num'的值改為1.
- 方法結(jié)束蛹尝,方法外打印num的值后豫,由于num內(nèi)存中的值沒有改變,還是0箩言,所以打印是0.
我們再解釋封裝類型的傳遞:
- 虛擬機在堆中開辟了一個Product的內(nèi)存空間硬贯,內(nèi)存中包含proName和num。
- 虛擬機在棧中分配給p一個內(nèi)存地址陨收,這個地址中存的是1中的Product的內(nèi)存地址饭豹。
- 虛擬機復(fù)制了一個p,我們叫他p',p和p'的內(nèi)存地址不同务漩,但它們存的值是相同的拄衰,都是1中Product的內(nèi)存地址。
- 將p'傳入方法饵骨,方法改變了1中的proName和num翘悉。
- 方法結(jié)束,方法外打印p中變量的值居触,由于p和p'中存的都是1中Product的地址妖混,但是1中Product里的值發(fā)生了改變老赤,
所以,方法外打印p的值制市,是方法執(zhí)行以后的抬旺。我們看到的效果是封裝類型的值是改變的。
最后我們再來解釋String在傳遞過程中的步驟:
- 虛擬機在堆中開辟一塊內(nèi)存祥楣,并存值"ab"开财。
- 虛擬機在棧中分配給str一個內(nèi)存,內(nèi)存中存的是1中的地址误褪。
- 虛擬機復(fù)制一份str责鳍,我們叫str',str和str'內(nèi)存不同兽间,但存的值都是1的地址历葛。
- 將str'傳入方法體
- 方法體在堆中開辟一塊內(nèi)存,并存值"cd"
- 方法體將str'的值改變渡八,存入5的內(nèi)存地址
- 方法結(jié)束啃洋,方法外打印str,由于str存的是1的地址屎鳍,所有打印結(jié)果是"ab"
這樣我們理解了java在方法傳參的整個過程。其實還是上面那句比較重要的話 java在方法傳遞參數(shù)時问裕,是將變量復(fù)制一份逮壁,然后傳入方法體去執(zhí)行。