在程序设计语言中,参数传递方法一般有两种
按值调用(call by value):方法接收的是调用者提供的值。
按引用调用(call by value):方法接收的是调用者提供的地址。
很多人认为,在Java中也存在两种参数传递的方法:
对于基本数据类型,Java中采用的是值传递;对于对象,Java中采用的是引用传递。
先看一段代码:
int a = 0;
int b = 1;
swap(a, b);
System.out.println("In Main: a=" + a + "; b=" + b);
public void swap(int a, int b) {
a = a + b;
b = a - b;
a = a - b;
System.out.println("In Swap: a=" + a + "; b=" + b);
}
运行结果如下:
In Swap: a=1; b=0
In Main: a=0; b=1
很显然,在swap方法中的参数是两个基本数据类型,最终的运行结果也验证了我们的猜想,对于基本数据类型,Java中进行的是值传递。但是对象在Java中是传递的引用吗?
//定义一个类Dog
class Dog {
String name = "";
public Dog(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return name;
}
}
//方法changeDogName
public void changeDogName(Dog dog, String name) {
dog.setName(name);
}
//创建一个名为"Peter"的Dog对象,然后修改它的名字。
Dog A = new Dog("Peter");
changeDogName(A, "Sam");
System.out.println("In Main: A=" + A + ";");
运行结果为:
In Main: A=Sam;
按结果来看,对于方法changeDogName,参数貌似就是按照引用调用的。
再看一个方法:
Dog A = new Dog("Peter");
Dog B = new Dog("Kaka");
swap(A,B);
System.out.println("In Main: A=" + A + "; B=" + B);
public static void swap(Dog x, Dog y) {
Dog temp = x;
x = y;
y = temp;
}
如果按照Java中对象作为参数时是传递的引用,那么最终的结果,A和B一定会进行交换。但是运行之后发现:
In Main: A=Peter; B=Kaka
A和B的并没有按照我们所想象的那种交换,这种结果倒是很符合按值调用。
其实,在swap(Dog x, Dog y)中,参数x和y被初始化为main方法中两个对象A和B引用的拷贝,也就是地址的拷贝。而在方法swap()中,实际交换的是这两个拷贝。
- 最初,A指向堆内存中Dog(“Peter”)对象,A的值就是Dog(“Peter”)在堆内存中的地址,同理B指向堆内存中Dog(“Kaka”)对 象;
- 在调用方法swap(x,y)之后,x和y分别指向堆内存中的Dog(“Peter”)和Dog(“Kaka”),x和y分别是A和B地址值的两个副本(拷贝),x和y存在于栈内存中(对象存在于堆内存);
- 在方法swap执行时,x和y的地址值进行了交换,y指向Dog(“Kaka”),x指向Dog(“Peter”),但是对于A和B来说,这并没有任何的影响,A和B的地址值并没有任何改变,因此仅仅交换A和B的副本不会导致A和B自身发生改变,方法执行完成后,x和y就被会释放掉。
而在方法changeDogName(Dog dog, String name)中,参数dog同样是A的副本,它们都指向堆内存中Dog(“Peter”),对dog调用方法setName(String name),实际改变的是堆内存中Dog(”Peter”)的name属性,并不会改变A的地址值,A并没有被改变,只不过是A所指向的对象属性发生了改变。
在Java核心技术上解释道:Java程序设计语言对对象采用的不是引用调用,实际上,对象引用进行的是值传递。
其实最好的理解方法就是,不论是基本数据类型还是对象,传递的都是参数的副本。对于基本数据类型,副本就是原始参数的值;对于对象,副本就是参数的地址。