你真的真的理解Java的按引用傳遞嗎?

       今天在博客上看到《你真的理解Java的按引用傳遞嗎?》這篇博文,就好奇進去看了一下,結果發現,其實說了半天,並沒有特別清楚的解釋。尤其是對於傳遞String類型時的例子時,有點發蒙。

       接觸Java也有好幾年了,本來以爲這些簡單自己早就懂了,結果在看到最後一個例子時,直接就把答案給猜錯了。

[java] view plain copy
 print?
  1. public class Test5{  
  2.     public static void main(String[] args) {  
  3.         Object o = new Object();  
  4.         System.out.println(o);  
  5.         change(o);  
  6.         System.out.println(o);  
  7.     }  
  8.     public static void change(Object o){  
  9.         o = null;  
  10.     }  
  11. }  
       上面這個demo的結果是:
[java] view plain copy
 print?
  1. java.lang.Object@2a139a55  
  2. java.lang.Object@2a139a55  
       而不是
[java] view plain copy
 print?
  1. java.lang.Object@2a139a55  
  2. null  

       仔細研究了一下,現在終於是弄懂了。原來以前都是自以爲懂了。

       先不解析上面這個demo,我們拿下面這個來說明:

[java] view plain copy
 print?
  1. public class AAA {  
  2.     private String str = "123";  
  3.     public static void main(String[] args) {  
  4.         AAA aaa = new AAA();  
  5.         System.out.println(aaa.str);  
  6.         aaa.change(aaa);  
  7.         System.out.println(aaa.str);  
  8.         System.out.println("-------------------------------");  
  9.         AAA bbb = new AAA();  
  10.         System.out.println(bbb.str);  
  11.         bbb.change2(bbb);  
  12.         System.out.println(bbb.str);  
  13.     }  
  14.     public void change(AAA a) {  
  15.         a.str = "abc";  
  16.     }  
  17.     public void change2(AAA a) {  
  18.         a = new AAA();  
  19.         a.str = "abc";  
  20.     }  
  21. }  
       答案是什麼呢:


       爲什麼不一樣,按照在書上看到的——“java傳遞的是引用,也就是對象的地址”,那change方法把源對象的str屬性給改了,爲什麼change2不能改呢?


       第一副圖,對應對象aaa,第二副圖對應對象bbb。

       第一副:首先new了一個對象aaa,裏面有一個str,指向字符串“123”的地址。然後調用change(aaa)後,由於java方法傳遞的是地址值的拷貝,所以參數a對象也是指向了aaa原來的堆空間。然後更改str,把參數a中str的指向了“abc”所在空間,即把aaa的str的地址給換成了“abc”,所以最後在輸出的時候就會輸出“abc”了。

       第二副:首先new了一個對象bbb,裏面有一個str,指向字符串“123”的地址。然後調用change(bbb)後,由於java方法傳遞的是地址值的拷貝,所以參數a對象也是指向了aaa原來的堆空間。但是,方法裏調用了b = new AAA();,這樣就把b指向的地址換成了一個新對象的地址,同時裏面有一個str,由於也是“123”,所以指向的是同一個“123”所屬的地址。然後更改str,只是把參數對象a的str的地址給換成了“abc”(雙線粗箭頭),但是bbb對象中的str仍然沒有變化,所以最後在輸出的時候還是輸出“123”了。


       所以說在傳遞對象類型時,就看在方法裏參數的地址在修改其他屬性前有沒有發生改變:

  • 如果形參的(引用)地址發生了改變,那對原對象就沒有影響;
  • 如果形參的(引用)地址未改變,但屬性有改變,原對象的屬性會有隨之改變;
  • 如果屬性先改變,形參的(引用)地址後改變,那原對象的屬性在地址改變前有修改的會隨之改變。

       所以我們會經常看到下面的這樣的例子:

[java] view plain copy
 print?
  1. public static void main(String[] args) {  
  2.     BBB bbb = new BBB();  
  3.     List<Object> list = new ArrayList<Object>();  
  4.     System.out.println(list.size());  
  5.     bbb.change(list);  
  6.     System.out.println(list.size());  
  7. }  
  8. public void change(List<Object> list) {  
  9.     for (int i = 0; i < 3; i++) {  
  10.         list.add("00"+i);  
  11.     }  
  12. }  
       最後輸出list.size=3,因爲list的地址沒有發生變化,所以方法中對list增刪對象,原對象就會有變化。


       所以現在再看傳遞String的這個方法,是不是就簡單多了。因爲在方法中參數的地址改變了,所以就不會影響到源對象了。所以還是輸出“123”。

[java] view plain copy
 print?
  1. public class Test2{  
  2.     public static void main(String[] args) {  
  3.         String str = "123";  
  4.         System.out.println(str);  
  5.         change(str);  
  6.         System.out.println(str);  
  7.     }  
  8.     public static void change(String str){  
  9.         str = "abc";  
  10.     }  
  11. }  

       那再回頭看看文章裏第一個例子是不是就也清楚了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章