概念
pass-by-value(按值傳遞):方法調用時,傳遞參數會在內存中開闢新的空間來存儲參數,實際參數把它的值傳遞給對應的形式參數,函數接收的是原始值的一個copy,此時內存中存在兩個相同的變量,即實際參數和形式參數,後面方法中的操作都是對形參這個值的修改,不影響實際參數的值。離開函數體,不影響參數本身
pass-by-reference(引用傳遞):引用傳遞,則方法接收的是調用者提供的實際參數的地址,每次傳遞變量時,直接傳遞棧地址,而不做複製,方法體內和外指向同一個內存空間,裏面改外面也會變。
背景知識
對基本數據類型,值就直接保存在變量中;對引用類型,變量中保存的只是實際對象的地址,一般稱這種變量爲"引用",引用指向實際對象,實際對象中保存着內容
JAVA中的值傳遞與引用傳遞
JAVA中只存在值傳遞!
JAVA程序設計語言總是採用值調用。也就是說,方法得到的是所有參數值的一個拷貝,特別是,方法不能修改傳遞給它的任何參數變量的內容
* 對基本類型(byte,short,int,long,float,double,char,boolean,char)的函數參數傳遞是按值傳遞,一個方法是不可能修改一個基本數據類型的參數
public class Test {
private static int x=10;
public static void updateValue(int value){
value = 3 * value;
}
public static void main(String[] args) {
System.out.println("調用前x的值:"+x);
updateValue(x);
System.out.println("調用後x的值:"+x);
}
}
運行結果:
調用前x的值:10
調用後x的值:10
執行過程:
分析:
value被初始化爲x值的一個拷貝(也就是10);value被乘以3後等於30,但注意此時x的值仍爲10;這個方法結束後,參數變量value不再使用,被回收
* 對於非基本類型(數組,類,接口)的函數參數傳遞傳遞的是對象引用的拷貝,即傳遞的是引用變量內存儲的對象內容的地址值,所以還是按值傳遞
對於引用類型的變量而言,它們的“內容”存放在託管堆裏,而在線程棧裏存放的是“內容”的“地址”。如對象在託管堆中的地址是x0001,那麼棧地址0001的內存空間中存放的就是這個對象的內存地址x0001
注:當傳遞方法參數類型爲引用數據類型時,一個方法不修改引用變量本身,但可以修改一個引用數據類型的參數所指向對象的值
public class Test {
private static User user=null;
public static void updateUser(User student){
student.setName("Lishen");
student.setAge(18);
}
public static void main(String[] args) {
user = new User("zhangsan",26);
System.out.println("調用前user的值:"+user.toString());
updateUser(user);
System.out.println("調用後user的值:"+user.toString());
}
}
class User {
private String name;
private int age;
public User(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 "Name: " + name + " Age: " + age;
}
}
運行結果:
調用前user的值:Name: zhangsan Age: 26
調用後user的值:Name: Lishen Age: 18
執行過程:
分析:
student變量被初始化爲引用user值的拷貝;調用student變量的set方法作用在這個引用的對象上,user和student同時引用的User對象內部值被修改;方法結束後,student變量不再使用,被釋放,而user還是沒有變,依然指向User對象
public class Test {
private static User user=null;
private static User stu=null;
public static void swap(User x,User y){
User temp =x;
x=y;
y=temp;
}
public static void main(String[] args) {
user = new User("user",26);
stu = new User("stu",18);
System.out.println("調用前user的值:"+user.toString());
System.out.println("調用前stu的值:"+stu.toString());
swap(user,stu);
System.out.println("調用後user的值:"+user.toString());
System.out.println("調用後stu的值:"+stu.toString());
}
}
運行結果:
調用前user的值:Name: user Age: 26
調用前stu的值:Name: stu Age: 18
調用後user的值:Name: user Age: 26
調用後stu的值:Name: stu Age: 18
執行過程:
分析:
swap方法的參數x和y被初始化爲兩個對象引用的拷貝,這個方法交換的是這兩個拷貝的值而已,最終在方法結束後x,y將被丟棄,而原來的變量user和stu仍然引用這個方法調用之前所引用的對象。這個過程也充分說明了JAVA對對象採用的不是引用調用,而是對象引用進行的是值傳遞,一個方法是不可能修改參數本身的。
* 不可變類String\Integer\Double等
不可變類因爲沒有提供自身修改的函數,創建後,一旦更改等於是重新開了一塊內存地址新生成一個對象
public static void change(String string2){
string2="zhangsan";
}
public static void main(String[] args) {
String string1=new String("abcd");
System.out.println(string1);
change(string1);
System.out.println(string1);
}
運行結果:
abcd
abcd
分析:
調用change函數後,兩個字符串指向同一個Heap上的地址,string2修改內容後指向了新地址,但是string1仍然指向原地址