如果你學的第一門程序語言是java可能對這個傳遞方式沒有那么敏感,如果學了c或c++,然后再學java,那么可能對這個問題會感到困惑。
1.值傳遞與引用傳遞的概念
在將傳遞方式之前先理解一下形參與實參。
形式參數:是在定義函數名和函數體的時候使用的參數,目的是用來接收調用該函數時傳入的參數。
實際參數:在調用有參函數時,主調函數和被調函數之間有數據傳遞關系。在主調函數中調用一個函數時,函數名后面括號中的參數稱為“實際參數”。
可以這么理解:形參是實參的抽象,實參是調用時的參數,形參是定義函數的參數
值傳遞:方法調用時,實際參數把它的值傳遞給對應的形式參數,函數接收的是原始值的一個copy,此時內存中存在兩個相等的基本類型,即實際參數和形式參數,后面方法中的操作都是對形參這個值的修改,不影響實際參數的值。
引用傳遞:是指在調用函數時將實際參數的地址直接傳遞到函數中,那么在函數中對參數所進行的修改,將影響到實際參數。
划重點:值傳遞和引用傳遞的主要區別
值傳遞 |
引用傳遞 |
創建副本,在函數體中不能改變原來的值 |
不創建副本,在在函數體中不能改變原來的值 |
創建副本的含義看圖:
創建副本也就是說,把要調用的實參先拷貝一份出來,然后用拷貝的那一份傳進函數體內。不創建副本時,不會發生copy這個步驟。
舉個值傳遞的栗子:
1 public class Test01 { 2 public static void main(String[] args) { 3 int a=1,b=2; 4 swap(a, b); 5 System.out.println("a="+a); 6 System.out.println("b="+b); 7 } 8 public static void swap(int a,int b) { 9 int temp=a; 10 a=b; 11 b=temp; 12 } 13 }
結果:
1 a=1 2 b=2
上面栗子中,在函數中讓實現a,b交換,但調用函數后,輸出的結果仍然是a,b原來的值,說明函數體中的操作並不能影響a,b在函數體外的值。
引用傳遞的栗子就不測試了,有興趣的話可以用c++來測試,參數為定義為別名或指針時,在c++中是引用傳遞。
誤區:傳遞的參數如果是普通類型,那就是值傳遞,如果是對象,那就是引用傳遞。這是錯誤的!!!!!
2.java中的值傳遞
在java中,無論參數是基本類型,還是引用數據類型,都是值傳遞方式。下面來舉個引用數據類型的參數,基本數據類型傳參的栗子上面已經有了。
1 public class Test01 { 2 public static void swap(Student st1,Student st2) { 3 Student temp=st1; 4 st1=st2; 5 st2=temp; 6 } 7 8 public static void main(String[] args) { 9 Student st1=new Student("張三",20); 10 Student st2=new Student("李四",20); 11 swap(st1, st2); 12 System.out.println("st1:"+st1); 13 System.out.println("st2:"+st2); 14 }
結果:
1 st1:Student [name=張三, age=20] 2 st2:Student [name=李四, age=20]
例子中,st1和st2的所指向的對象並沒有發生改變。
這時候,你可能會問,既然java是值傳遞,那么實參會發生拷貝,那拷貝的是什么東西呢?答案是:拷貝的是對象在堆中的地址。來個栗子來驗證一下:
1 public class Test01 { 2 public static void print(Student stu1,Student stu2) { 3 Student temp=stu1; 4 stu1=stu2; 5 stu2=temp; 6 System.out.println("在函數體中交換后打印stu1: "+stu1); 7 System.out.println("在函數體中交換后打印stu2: "+stu2); 8 } 9 10 public static void main(String[] args) { 11 Student stu1=new Student("stu1",20); 12 Student stu2=new Student("stu2",30); 13 print(stu1, stu2); 14 } 15 }
結果:
1 在函數體中交換后打印stu1: Student [name=stu2, age=30] 2 在函數體中交換后打印stu2: Student [name=stu1, age=20]
從結果中可以看出,在函數體中stu1和stu2所指向的對象確實是發生了改變,這是因為在值傳遞的過程中拷貝了他們在堆中的地址。
來看看他們在內存中是怎么樣的:
這時候你可能會問,既然java的值傳遞是拷貝地址,那我能不能改變地址所指向的內容?答案是:當然可以
1 public static void changeInf(Student stu) { 2 stu.setName("我改名字了"); 3 } 4 5 public static void main(String[] args) { 6 Student stu=new Student("張三",18); 7 changeInf(stu); 8 System.out.println(stu); 9 }
結果:
1 Student [name=我改名字了, age=18]
對象內容改變了
結論:java中只有值傳遞,這可能是因為java沒有指針和別名引用的原因吧。