解惑4:java是值傳遞還是引用傳遞
阿新 • • 發佈:2020-08-05
## 一、概述
曾經糾結了很久java的引數傳遞方式是什麼樣的,後面粗略的瞭解了一鱗半爪以後有了大概的印象:“傳引數就是值傳遞,傳物件就是引用傳遞”,後面進一步查找了相關資料和文章以後,發現這麼理解是不正確的。
這裡先放結論:
- java中引數的傳遞可以理解為**都是值傳遞**
- 基礎資料型別傳遞的是**值的拷貝**
- 物件型別是共享物件傳遞,傳遞的是**地址的拷貝**
## 二、形參和實參
要理解引數的傳遞就必須先理解形參和實參:
- 形參:就是形式引數,用於**定義方法的時候使用的引數**,是用來接收呼叫者傳遞的引數的。
形參只有在方法被呼叫的時候,虛擬機器才會分配記憶體單元,在方法呼叫結束之後便會釋放所分配的記憶體單元。
因此,**形參只在方法內部有效**,所以針對引用物件的改動也無法影響到方法外。
- 實參:就是實際引數,用於呼叫時**傳遞給方法的引數**。
舉個例子:
~~~java
public static void main( String[] args ) {
String string = "Hello";
//string是實際引數
sout(string);
}
public static void sout(String str){
//str為形式引數
System.out.println(str);
}
~~~
## 三、值傳遞和引用傳遞與共享物件傳遞
### 1.值傳遞和引用傳遞
理解了實參和形參,以及java對應的資料型別,我們就可以理解值傳遞和引用傳遞了。
- 值傳遞:方法呼叫時,實際引數的**值**被傳遞給對應的形式引數,函式接收的是原始值的一個copy, 此時記憶體中存在兩個相等的基本型別,即實際引數和形式引數,後面方法中的操作**都是對形參這個值的修改,不影響實際引數的值**。
- 引用傳遞/址傳遞:方法呼叫時,實際引數的**地址**被傳遞給方法中相對應的形式引數,函式接收的是原始值的記憶體地址。在方法執行中,**形參和實參內容相同,指向同一塊記憶體地址,方法執行中對引用的操作將會影響到實際物件**。
對於這兩種方式,網上有一個非常形象的圖:
![](http://img.xiajibagao.top/值傳遞還是引用傳遞.gif)
### 2.共享物件傳遞
但是java的傳值策略有點類似於兩者的結合,是**共享物件傳遞**:
- 共享物件傳遞:先獲取到實際引數的地址,然後將其複製,並把**該地址的拷貝**傳遞給被調函式的形式引數。因為引數的地址都指向同一個物件,所以我們稱也之為"傳共享物件",所以,如果在被調函式中改變了形式引數的值,呼叫者是可以看到這種變化的。
**這也是之所以說java也是值傳遞的原因,共享物件傳遞實際上也是對實參進行拷貝然後賦給形參,但是操作針對的物件不是值而是地址**!
由於傳遞的是地址的拷貝,所以如果你在方法中將這個地址指向了新的物件,實際上是沒有任何對方法外是沒有任何作用的,舉個例子:
~~~java
public static void main( String[] args ) {
Person p = new Person();
System.out.println("main中:" + p.hashCode());
change(p);
System.out.println("main中:" + p.hashCode());
}
public static void change(Person person){
person = new Person();
System.out.println("change中:" + person.hashCode());
}
//輸出
main中:692404036
change中:1554874502
main中:692404036
~~~
可以看到在`main`方法中輸出的hashCode指向的都是同一個物件,而`change`中指向了另一個,可以這麼理解:
- p為指向了第一個Person物件的地址
- 把p拷貝了一份得到p‘,這裡的p’就是`change`方法中的形參p
- `change`中p指向了一個新的Person物件,在`change`這個函式範圍裡p指向的就是new出來的第二個Person物件的地址
- 由於`change`中的p實際上是`main`中p的拷貝p‘,所以在`change`裡p'指向的改變對`main`中的p不會有任何影響
## 四、總結
> 你在福建有座倉庫,給自己配了一把鑰匙
1.三種傳遞:
- 值傳遞:你建了一座一模一樣的倉庫給別人
- 引用傳遞:把你家倉庫的鑰匙給了別人
- 共享物件傳遞:把你家倉庫鑰匙復刻了一把給別人
2.共享物件傳遞的特點:
- 拷貝的地址與原地址指向同一個記憶體物件:別人用你復刻的鑰匙一樣能進出你的倉庫
- 拷貝地址引用物件的改變不影響原地址的引用物件:別人在山東也蓋了個倉庫,用你給他的鑰匙配了鎖,他的鑰匙在山東只能開他的倉庫,你的鑰匙在福建只能開你