java基礎–(11)–關於Java中的值傳遞與“引用傳遞”
結論:Java中都是值傳遞,沒有“引用傳遞”
(1)基本資料型別傳值,對形參的修改不會影響實參;
(2)引用型別傳引用的值,形參和實參指向同一個記憶體地址(同一個物件),所以對引數的修改會影響到實際的物件;
(3)String, Integer, Double等immutable的型別特殊處理,可以理解為傳值,最後的操作不會修改實參物件。
下面來進行詳解:
首先回顧一下在程式設計語言中有關將引數傳遞給方法(或函式)的一些專業術語。按值呼叫(call by value)表示方法接收的是呼叫者提供的值,而按引用呼叫(call by reference)表示方法接收的是呼叫者提供的變數地址。一個方法可以修改傳遞引用所對應的變數值,而不能修改傳遞值呼叫所對應的變數值。
Java程式設計語言總是採用按值呼叫。也就是說,方法得到的是所有引數值的一個拷貝,也就是說,方法不能修改傳遞給它的任何引數變數的內容。
Example1
@Test public void testInt() { int num1 = 10; int num2 = 20; swapInt(num1, num2); System.out.println("num1 = " + num1); System.out.println("num2 = " + num2); } private void swapInt(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 的副本,副本的內容無論怎麼修改,都不會影響到原件本身。另外即使是String, Integer, Double等immutable,也會是上面的情況,請看Example2
Example2
@Test public void testString() { String str1 = "hello"; String str2 = "world"; swapString(str1, str2); System.out.println("str1 = " + str1); System.out.println("str2 = " + str2); } private void swapString(String a, String b) { String temp = a; a = b; b = temp; System.out.println("a = " + a); System.out.println("b = " + b); } 結果: a = world b = hello str1 = hello str2 = world
通過上面的例子,我們已經知道了一個方法不能修改一個基本資料型別的引數,而物件引用作為引數不一樣,請看 Example3
Example3
@Test
public void testArr() {
int[] arr = { 1, 2, 3, 4, 5 };
System.out.println("改變前arr[0] = " + arr[0]);
changeArr(arr);
System.out.println("改變後arr[0] = " + arr[0]);
}
private void changeArr(int[] array) {
// 將陣列的第一個元素變為0
array[0] = 666;
}
結果:
改變前arr[0] = 1
改變後arr[0] = 666
解析:
array 被初始化 arr 的拷貝也就是一個物件的引用,也就是說 array 和 arr 指向的時同一個陣列物件。 因此,外部對引用物件的改變會反映到所對應的物件上。
通過 example3 我們已經看到,實現一個改變物件引數狀態的方法並不是一件難事。理由很簡單,方法得到的是物件引用的拷貝,物件引用及其他的拷貝同時引用同一個物件。
很多程式設計語言(特別是,C++和Pascal)提供了兩種引數傳遞的方式:值呼叫和引用呼叫。有些程式設計師認為Java程式設計語言對物件採用的是引用呼叫,實際上,這種理解是不對的。由於這種誤解具有一定的普遍性,所以下面給出一個反例來詳細地闡述一下這個問題。即使傳遞的是一個物件的引用也不會改變原有物件的值
Example4
@Test
public void testStudent() {
Student s1 = new Student("小張");
Student s2 = new Student("小李");
swapStudent(s1, s2);
System.out.println("s1.getName() = " + s1.getName());
System.out.println("s2.getName() = " + s2.getName());
}
private void swapStudent(Student x, Student y) {
Student temp = x;
x = y;
y = temp;
System.out.println("x.getName() = " + x.getName());
System.out.println("y.getName() = " + y.getName());
}
結果:
x.getName() = 小李
y.getName() = 小張
s1.getName() = 小張
s2.getName() = 小李
解析:
交換之前:
交換之後:
通過上面兩張圖可以很清晰的看出: 方法並沒有改變儲存在變數 s1 和 s2 中的物件引用。swap方法的引數x和y被初始化為兩個物件引用的拷貝,這個方法交換的是這兩個拷貝。但是我們可以改變引用的物件的值,擴充套件如下
Example5
@Test
public void testStudent2() {
Student s1 = new Student("張三");
Student s2 = new Student("李四");
swapStudent2(s1, s2);
System.out.println("s1.getName() = " + s1.getName());
System.out.println("s2.getName() = " + s2.getName());
}
private void swapStudent2(Student x, Student y) {
x.setName("張三三");
y.setName("李四四");
System.out.println("x.getName() = " + x.getName());
System.out.println("y.getName() = " + y.getName());
}
結果:
x.getName() = 張三三
y.getName() = 李四四
s1.getName() = 張三三
s2.getName() = 李四四
上面的Example5我們發現,通過對x,y的改變可以改變原有的物件的值,主要是看我們是對形參的修改還是對形參指向的物件的修改,其中的區別能感覺到麼 ,如果沒有感覺到。
那就再看一遍Example4,Example5。
再次總結
Java程式設計語言對物件採用的不是引用呼叫,實際上,物件引用是按
值傳遞的。
下面再總結一下Java中方法引數的使用情況:
- 一個方法不能修改一個基本資料型別的引數(即數值型或布林型》
- 一個方法可以改變一個物件引數的狀態。
- 一個方法不能讓物件引數引用一個新的物件。
看這裡,看這裡
文章總目錄:部落格導航
碼字不易,尊重原創,轉載請註明:https://blog.csdn.net/u_ascend/article/details/82876848
參考博文:https://github.com/Snailclimb/JavaGuide/issues/12
如果涉及到侵權,請聯絡我:[email protected],1-2個工作日處理。
作者自己維護的公眾平臺,感興趣的來,需要什麼可以私聊我,網盤中有大量學習資源,後續慢慢新增。
歡迎互噴