前言
我們都知道,java中數據類型分爲基本數據類型和引用數據類型。
- 基本數據類型
- 整型:byte,short,int,long
- 浮點型:float,double
- 字符型:char
- 布爾型:boolean
- 引用數據類型
- 數組
- 類
- 接口
方法的參數分爲實際參數,和形式參數。
- 形式參數:定義方法時寫的參數。
- 實際參數:調用方法時寫的具體數值。
一般情況下,在數據做爲參數傳遞的時候,基本數據類型是值傳遞,引用數據類型是引用傳遞(地址傳遞)。
值傳遞
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);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
運行的結果是:
a = 20
b = 10
num1 = 10
num2 = 20
- 1
- 2
- 3
- 4
原因:
流程:
- 主函數進棧,num1、num2初始化。
- 調用swap方法,swap( )進棧,將num1和num2的值,複製一份給a和b。
- swap方法中對a、b的值進行交換。
- swap方法運行完畢,a、b的值已經交換。
- swap方法彈棧。
- 主函數彈棧。
解析:
在swap方法中,a、b的值進行交換,並不會影響到num1、num2。因爲,a、b中的值,只是從num1、num2的複製過來的。
也就是說,a、b相當於num1、num2的副本,副本的內容無論怎麼修改,都不會影響到原件本身。
引用傳遞
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) {
int len = array.length;
array[0] = 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
運行的結果是:
0
- 1
原因:
流程:
- 主函數進棧,int[] arr初始化。
- 調用change方法,change( )進棧,將arr的地址值,複製一份給array。
- change方法中,根據地址值,找到堆中的數組,並將第一個元素的值改爲0。
- change方法運行完畢,數組中第一個元素的值已經改變。
- change方法彈棧。
- 主函數彈棧。
解析:
調用change()的時候,形參array接收的是arr地址值的副本。並在change方法中,通過地址值,對數組進行操作。change方法彈棧以後,數組中的值已經改變。main方法中,打印出來的arr[0]也就從原來的1變成了0.
無論是主函數,還是change方法,操作的都是同一個地址值對應的數組。
就像你把自己家的鑰匙給了另一個人,這個人拿着鑰匙在你家一頓瞎折騰,然後走了。等你拿着鑰匙回到家以後,家裏已經變成了被折騰過後,慘不忍睹的樣子。。
這裏的鑰匙就相當於地址值,家就相當於數組本身。
String類型傳遞
public static void main(String[] args) {
String str = "AAA";
change(str);
System.out.println(str);
}
public static void change(String s) {
s = "abc";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
運行的結果是:
AAA
- 1
這就神奇了!!!
String是一個類,類是引用數據類型,做爲參數傳遞的時候,應該是引用傳遞。但是從結果看起來卻是值傳遞。
原因:
String的API中有這麼一句話:“their values cannot be changed after they are created”,
意思是:String的值在創建之後不能被更改。
API中還有一段: String str = "abc";
等效於:
char data[] = {'a', 'b', 'c'};
String str = new String(data);
也就是說:對String對象str的任何修改 等同於 重新創建一個對象,並將新的地址值賦值給str。
這樣的話,上面的代碼就可以寫成:
public static void main(String[] args) {
String str1 = "AAA";
change(str1);
System.out.println(str1);
}
public static void change(String s) {
char data[] = {'a', 'b', 'c'}
String str = new String(data);
s = str;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
流程:
- 主函數進棧,str1初始化。
- 調用change方法,change( )進棧,將str1的地址值,複製一份給s。
- change方法中,重現創建了一個String對象”abc”,並將s指向了新的地址值。
- change方法運行完畢,s所指向的地址值已經改變。
- change方法彈棧。
- 主函數彈棧。
解析:
String對象做爲參數傳遞時,走的依然是引用傳遞,只不過String這個類比較特殊。
String對象一旦創建,內容不可更改。每一次內容的更改都是重現創建出來的新對象。
當change方法執行完畢時,s所指向的地址值已經改變。而s本來的地址值就是copy過來的副本,所以並不能改變str1的值。
String類型類似情況:
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 p) {
Person person = new Person("李四");
p = person;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
運行的結果是:
張三
- 1
總結
- 值傳遞的時候,將實參的值,copy一份給形參。
- 引用傳遞的時候,將實參的地址值,copy一份給形參。
也就是說,不管是值傳遞還是引用傳遞,形參拿到的僅僅是實參的副本,而不是實參本身。