關於Java是值傳遞還是引用傳遞,可以從程式碼層面來實現一下拿到結果
執行下面的程式碼:
public static void main(String[] args) {
int num = 10;
String name = "Tom";
modify(num, name);
System.out.println("第3次列印int:" + num);
System.out.println("第3次列印String:" + name);
System.out.println("------------------------------------");
}
public static void modify(int n, String str){
System.out.println("第1次列印int:" + n);
System.out.println("第1次列印String:" + str);
System.out.println("------------------------------------");
// 嘗試在方法內部修改傳進來的引數
n = 999;
str = "ABC";
System.out.println("第2次列印int:" + n);
System.out.println("第2次列印String:" + str);
System.out.println("------------------------------------");
}
列印出來的結果如下:
第1次列印int:10
第1次列印String:Tom
------------------------------------
第2次列印int:999
第2次列印String:ABC
------------------------------------
第3次列印int:10
第3次列印String:Tom
------------------------------------
可以看到無論是基本型別還是引用型別,傳引數進去的時候的值和執行完modify方法後的值是一樣的,也就是第1次列印和第三次列印是一樣的。可是為什麼明明在第2次已經修改成功了,第3次卻又變回去了呢?
嘗試換個方法把引數拿出來,
public static void main(String[] args) {
int num = 10;
String name = "Tom";
int modifiedNum = modifyAndReturn(num);
String modifiedName = modifyAndReturn(name);
System.out.println("列印num:" + num);
System.out.println("列印name:" + name);
System.out.println("------------------------------------");
System.out.println("列印modifiedNum:" + modifiedNum);
System.out.println("列印modifiedName:" + modifiedName);
}
public static int modifyAndReturn(int n){
System.out.println("modifyAndReturn第1次列印int:" + n);
// 嘗試在方法內部修改傳進來的引數
n = 999;
System.out.println("modifyAndReturn第2次列印int:" + n);
System.out.println("------------------------------------");
return n;
}
public static String modifyAndReturn(String str){
System.out.println("modifyAndReturn第1次列印String:" + str);
// 嘗試在方法內部修改傳進來的引數
str = "ABC";
System.out.println("modifyAndReturn第2次列印String:" + str);
System.out.println("------------------------------------");
return str;
}
得到的結果為
modifyAndReturn第1次列印int:10
modifyAndReturn第2次列印int:999
------------------------------------
modifyAndReturn第1次列印String:Tom
modifyAndReturn第2次列印String:ABC
------------------------------------
列印num:10
列印name:Tom
------------------------------------
列印modifiedNum:999
列印modifiedName:ABC
可以看到透過return出來的值,的確是被改變了的,那又是為什麼導致這個改變沒有應用到引數本體呢?修改下程式碼再次測試
public static void main(String[] args) {
int num = 10;
String name = "Tom";
// 列印num和str的地址
System.out.println("修改前,傳參前:");
System.out.println(System.identityHashCode(num));
System.out.println(System.identityHashCode(name));
System.out.println("---------------------------");
printAddr(num, name);
System.out.println("---------------------------");
System.out.println("修改後,執行完方法後:");
System.out.println(System.identityHashCode(num));
System.out.println(System.identityHashCode(name));
}
public static void printAddr(int n, String str){
// 列印n和str的地址
System.out.println("修改前,傳參後:");
System.out.println(System.identityHashCode(n));
System.out.println(System.identityHashCode(str));
n = 999;
str = "ABC";
// 列印n和str的地址
System.out.println("---------------------------");
System.out.println("修改後,傳參後:");
System.out.println(System.identityHashCode(n));
System.out.println(System.identityHashCode(str));
}
執行結果如下
修改前,傳參前:
1324119927
990368553
---------------------------
修改前,傳參後:
1324119927
990368553
---------------------------
修改後,傳參後:
1096979270
1078694789
---------------------------
修改後,執行完方法後:
1324119927
990368553
可以看到傳參進來的引數地址是和外部定義的地址是同一個,但是修改之後會指向另一個新的地址,導致原來地址上的資料不會受到影響,這其實是一個保護機制,防止引數傳入方法內被篡改指向。
下面演示引用型別的另一種情況,一些老鐵可能以為是對引用型別本身的修改,其實這是不對的。
先定義一個類Person
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
執行下面的程式碼,可以看到傳進去的引數的屬性被改變
public static void main(String[] args) {
Person person = new Person("Rosy", 24);
String [] strings = {"AAA", "BBB", "CCC"};
System.out.println("第1次列印:");
System.out.println(person);
System.out.println(Arrays.toString(strings));
modifyObjAndPrintValue(person, strings);
System.out.println("---------------------------");
System.out.println("第4次列印:");
System.out.println(person);
System.out.println(Arrays.toString(strings));
}
public static void main5(String[] args) {
Person person = new Person("Rosy", 24);
String [] strings = {"AAA", "BBB", "CCC"};
System.out.println("第1次列印:");
System.out.println(System.identityHashCode(person));
System.out.println(System.identityHashCode(person.getAge()));
System.out.println(System.identityHashCode(person.getName()));
System.out.println(System.identityHashCode(strings));
modifyObj(person, strings);
System.out.println("---------------------------");
System.out.println("第4次列印:");
System.out.println(System.identityHashCode(person));
System.out.println(System.identityHashCode(person.getAge()));
System.out.println(System.identityHashCode(person.getName()));
System.out.println(System.identityHashCode(strings));
}
public static void modifyObjAndPrintValue(Person person, String [] strings){
System.out.println("---------------------------");
System.out.println("第2次列印:");
System.out.println(person);
System.out.println(Arrays.toString(strings));
person.setAge(1024);
person.setName("ABC");
strings[0] = "XXX";
System.out.println("---------------------------");
System.out.println("第3次列印:");
System.out.println(person);
System.out.println(Arrays.toString(strings));
}
執行結果為
第1次列印:
Person{name='Rosy', age=24}
[AAA, BBB, CCC]
---------------------------
第2次列印:
Person{name='Rosy', age=24}
[AAA, BBB, CCC]
---------------------------
第3次列印:
Person{name='ABC', age=1024}
[XXX, BBB, CCC]
---------------------------
第4次列印:
Person{name='ABC', age=1024}
[XXX, BBB, CCC]
從結果可以發現,Person物件的屬性都被修改,String陣列的元素也被修改,說明引數裡對屬性或陣列的修改是會影響物件本身的,具體可以列印地址再檢視一下:
public static void main(String[] args) {
Person person = new Person("Rosy", 24);
String [] strings = {"AAA", "BBB", "CCC"};
System.out.println("第1次列印:");
System.out.println(System.identityHashCode(person));
System.out.println(System.identityHashCode(person.getAge()));
System.out.println(System.identityHashCode(person.getName()));
System.out.println(System.identityHashCode(strings));
System.out.println(System.identityHashCode(strings[0]));
modifyObjAndPrintAddr(person, strings);
System.out.println("---------------------------");
System.out.println("第4次列印:");
System.out.println(System.identityHashCode(person));
System.out.println(System.identityHashCode(person.getAge()));
System.out.println(System.identityHashCode(person.getName()));
System.out.println(System.identityHashCode(strings));
System.out.println(System.identityHashCode(strings[0]));
}
public static void modifyObjAndPrintAddr(Person person, String [] strings){
System.out.println("---------------------------");
System.out.println("第2次列印:");
System.out.println(System.identityHashCode(person));
System.out.println(System.identityHashCode(person.getAge()));
System.out.println(System.identityHashCode(person.getName()));
System.out.println(System.identityHashCode(strings));
System.out.println(System.identityHashCode(strings[0]));
person.setAge(1024);
person.setName("ABC");
strings[0] = "XXX";
System.out.println("---------------------------");
System.out.println("第3次列印:");
System.out.println(System.identityHashCode(person));
System.out.println(System.identityHashCode(person.getAge()));
System.out.println(System.identityHashCode(person.getName()));
System.out.println(System.identityHashCode(strings));
System.out.println(System.identityHashCode(strings[0]));
}
第1次列印:
990368553
1096979270
1078694789
1831932724
1747585824
---------------------------
第2次列印:
990368553
1096979270
1078694789
1831932724
1747585824
---------------------------
第3次列印:
990368553
1023892928
558638686
1831932724
1149319664
---------------------------
第4次列印:
990368553
2093631819
558638686
1831932724
1149319664
從地址上可以看到,person和strings的地址一直沒有變過。而在引數內部修改的person屬性和陣列元素,會對這部分成員的地址進行修改,並且會應用到物件本體上。
總結下來就是,無論傳的是基本型別還是引用型別,只要在方法內部嘗試改變引數地址的,都只能在方法內部使用,不會影響本體,而在方法內部改變屬性的,會把對應的改變應用到本體上。所以Java是值傳遞的,傳參的時候並不是把本身傳入,而是建立一個副本,當被修改指向的時候不會影響本身,修改屬性由於不會修改本身的地址,因此的時候可以應用到本體上。