值傳遞與引用傳遞的理解一直不深,查看了很多的博客總結了自己的理解。如有錯誤,可以指出改正。
在瞭解引用傳遞之前,我們首先了解一下 什麼是引用,其作用到底是什麼?
1 引用是什麼?
可以說str這個變量是實例對象地址的一個別名
2 基本類型和引用類型
Java中數據類型分爲基本類型的引用類型兩大類
基本類型: byte、short、int、long、float、double、boolean、char
引用類型: 類、接口、數組
基本類型的變量在聲明時就會分配數據空間
而引用類型在聲明時只是給變量分配了引用空間,並不分配數據空間
3 值傳遞和引用傳遞
- 方法調用時,實際參數把它的值傳遞給方法的形參,形參接收的只是原始值的一個副本,後續方法裏對形參的修改不會影響原來的實參的值
- 在方法的執行過程中,形參和實參的內容相同,指向同一塊內存地址,也就是說操作的其實都是源數據,所以方法的執行將會影響到實際對象。
注意: 在JAVA中所有的傳遞其實都是值傳遞,只不過區分於理解,加了引用傳遞而已。(其實在引用傳遞的時候,是把實參的地址傳遞給了形參而已)
4 實際理解
4.1 值傳遞
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
運行結果:
a = 20
b = 10
num1 = 10
num2 = 20
理解:在swap方法中,a、b的值進行交換,並不會影響到num1、num2。因爲,a、b中的值,只是從num1、num2的複製過來的。
也就是說,a、b相當於num1、num2的副本,副本的內容無論怎麼修改,都不會影響到原件本身。
4.2 引用傳遞
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
change(arr);
System.out.println(arr[0]);
}
//將數組的第一個元素變爲0
public static void change(int[] array) {
array[0] = 0;
}
輸出結果:
0
解析:
假設arr指向的內存地址是0x11
實參arr黑色,形參array紅色。
調用change()的時候,形參array接收的是arr地址值的副本,此時的array也指向了0x11。
在change方法中對0x11地址裏面的值進行了修改,導致0x11地址中存儲的值變爲了0。
所以輸出爲arr[0]輸出結果爲0
在看一段代碼,我們都知道String和基本類型的包裝類都是不可變類。
- 什麼是不可變類?
就是該類被final修飾,創建了這個類的實例之後,就不容許改變他的值了。
看一下這些的源碼是否被final修飾。
在看如下的代碼解析過程。
public static void main(String[] args) {
String str = "Hello";
change(str);
System.out.println(str);
}
public static void change(String s) {
s = "wolrd";
}
輸出結果:
Hello
發現是不是和我們理解的,有點有出入?
在調用change方法的時候,str把他的地址副本拷貝了一份給了s。
此時的str和s都指向了一個字符串常量"hello"
那麼爲什麼s = "wolrd"的操作 並沒有影響到str呢?
String的API中有這麼一句話:“their values cannot be changed after they are created”,
意思是:String的值在創建之後不能被更改,因爲String是不可變類 被final修飾的。
API中還有一段:
String str = “abc”;
等效於:
char data[] = {‘a’, ‘b’, ‘c’};
String str = new String(data);
也就是說:對String對象str的任何修改 等同於 重新創建一個對象,並將新的地址值賦值給str。
實際上,可以轉換成一種理解。
public static void main(String[] args) {
String str = "Hello";
change(str1);
System.out.println(str1);
}
public static void change(String s) {
char data[] = {'w', 'o', 'r','l','d'};
String str = new String(data);
s = str;
}
然後引用指向的地址是。
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
public class Test {
public static void main(String[] args) {
Person p = new Person("張三");
change(p);
System.out.println(p.name);
}
public static void change(Person p1) {
Person person = new Person("李四");
p1 = person;
}
}
輸出結果:
張三
那麼爲什麼輸出結果是張三呢?看圖。
在調用change方法的時候 p和p1指向通一個內存地址。
在change方法裏面執行如下內容之後,p1的內存地址改變了。
Person person = new Person(“李四”);
最後的總結內容是:
值傳遞的時候,將實參的值,copy一份給形參。
引用傳遞的時候,將實參的地址值,copy一份給形參。
也就是說,不管是值傳遞還是引用傳遞,形參拿到的僅僅是實參的副本,而不是實參本身。