1. 程式人生 > >解惑4:java是值傳遞還是引用傳遞

解惑4:java是值傳遞還是引用傳遞

## 一、概述 曾經糾結了很久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.共享物件傳遞的特點: - 拷貝的地址與原地址指向同一個記憶體物件:別人用你復刻的鑰匙一樣能進出你的倉庫 - 拷貝地址引用物件的改變不影響原地址的引用物件:別人在山東也蓋了個倉庫,用你給他的鑰匙配了鎖,他的鑰匙在山東只能開他的倉庫,你的鑰匙在福建只能開你