1. 這里還有一個 例子就是 String,String他就相當於 值傳遞,其實他是 重新創建了堆中的內存地址:這哥們說的是: https://blog.csdn.net/party3/article/details/78648186
今天上班時,同事發現了一個比較有意思的問題。他把一個String類型的參數傳入方法,並在方法內改變了引用的值。 然后他在方法外使用這個值,發現這個String還是之前的值,並沒有改變。
這里要向大家介紹一下,大家都知道java在傳參時分為值 傳遞 和 引用傳遞 。參數為基本類型時是值傳遞, 參數為封裝類型時是引用傳遞。例如:
基本類型參數
1
2
3
4
5
6
7
8
9
10
11
|
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
;
}
}
|
打印的結果是num=0
。
封裝類型參數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
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;
}
}
|
運行的結果是:p.proName=after
和p.num=1
。
上面的兩個例子是明顯的值傳遞和引用傳遞。但是如果參數是String類型呢?我們看一下具體的例子:
1
2
3
4
5
6
7
8
9
10
11
|
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"
;
}
}
|
大家猜一下運行結果是什么呢?按照前面的例子,String應該是一個封裝類型,它應該是引用傳遞,是可以改變值得, 運行的結果應該是”cd”。我們實際運行一下看看,
str=ab
,這如何解釋呢?難道String是基本類型?也說不通呀。
這就要從java底層的機制講起了,java的內存模型分為 堆 和 棧 。
1.基本類型的變量放在棧里;
2.封裝類型中,對象放在堆里,對象的引用放在棧里。
java在方法傳遞參數時,是將變量復制一份,然后傳入方法體去執行。 這句話是很難理解的,也是解釋這個 問題的精髓。我們先按照這句話解釋一下基本類型的傳遞
- 虛擬機分配給num一個內存地址,並且存了一個值0.
- 虛擬機復制了一個num,我們叫他num’,num’和num的內存地址不同,但存的值都是0。
- 虛擬機講num’傳入方法,方法將num’的值改為1.
- 方法結束,方法外打印num的值,由於num內存中的值沒有改變,還是0,所以打印是0.
我們再解釋封裝類型的傳遞:
- 虛擬機在堆中開辟了一個Product的內存空間,內存中包含proName和num。
- 虛擬機在棧中分配給p一個內存地址,這個地址中存的是1中的Product的內存地址。
- 虛擬機復制了一個p,我們叫他p’,p和p’的內存地址不同,但它們存的值是相同的,都是1中Product的內存地址。
- 將p’傳入方法,方法改變了1中的proName和num。
- 方法結束,方法外打印p中變量的值,由於p和p’中存的都是1中Product的地址,但是1中Product里的值發生了改變, 所以,方法外打印p的值,是方法執行以后的。我們看到的效果是封裝類型的值是改變的。
最后我們再來解釋String在傳遞過程中的步驟:
- 虛擬機在堆中開辟一塊內存,並存值”ab”。
- 虛擬機在棧中分配給str一個內存,內存中存的是1中的地址。
- 虛擬機復制一份str,我們叫str’,str和str’內存不同,但存的值都是1的地址。
- 將str’傳入方法體
- 方法體在堆中開辟一塊內存,並存值”cd”
- 方法體將str’的值改變,存入5的內存地址
- 方法結束,方法外打印str,由於str存的是1的地址,所有打印結果是”ab”
這樣我們理解了java在方法傳參的整個過程。其實還是上面那句比較重要的話 java在方法傳遞參數時,是將變量復制一份,然后傳入方法體去執行。
歡迎訪問我的個人博客 www.liubo-tech.cn