在Java编程语言中,只有值传递,没有引用传递
首先,要明白什么是值传递,什么是引用传递。
①值传递的定义
就是传递变量的值。
②引用传递的定义
就是传递变量的地址。
现在,举例举例解释下值传递和引用传递,这里先用C++的语言对这两个概念进行解释,因为C++的世界中既有值传递,也有引用传递,而Java的世界里,只有值传递。
C++ 值传递的示例:
int main(void) //主函数
{
int a = 1 ;
int b = 2 ;
cout << "a = " << a << ", " << "b = " << b << endl ;//打印出调用Swap函数前a和b的值,a=1,b=2
Swap(a, b) ;
cout << "a = " << a << ", " << "b = " << b << endl ;//打印出调用Swap函数后a和b的值,a=1,b=2
system("pause") ;
return 0 ;
}
void Swap(int x, int y) //被调用的函数
{
int temp = x ;
x = y ;
y = temp ;
}
正如前面定义所说的那样,在调用Swap函数之前a=1,b=2,当调用Swap函数时,实参a把值的副本传给形参x,实参b也把值的副本传给形参y,之后就没有a和b什么事了。Swap函数里面的操作,实际上都是对x和y的操作。
具体如下图所示,在调用Swap函数之前,实参a的值是1,实参b的值是2,形参x和形参y里面没有放任何值。
当调用Swap函数时,实参a把自身的值拷贝一份,然后把拷贝的副本传递给形参x,实参b同理。
紧接着,进入Swap函数,int temp = x;就是再创建一个局部变量temp,把变量x的值赋值给变量temp。
然后把变量y的值赋值给变量x。
最后再把变量temp的值赋值给变量x。
图例的结果和程序打印出来的结果一样,a=1,b=2。整个过程就是值传递的具体实现,通过值传递,实参把自身的值拷贝出一个副本,然后把这个副本传递给形参,而被调函数的操作都是针对形参的,实参不会有任何影响。
C++ 引用传递的示例:
int main(void) //主函数
{
int a = 1 ;
int b = 2 ;
cout << "a = " << a << ", " << "b = " << b << endl ;//打印出调用Swap函数前a和b的值,a=1,b=2
Swap(a, b) ;
cout << "a = " << a << ", " << "b = " << b << endl ;//打印出调用Swap函数后a和b的值,a=2,b=1
system("pause") ;
return 0 ;
}
void Swap(int &x, int &y) //被调用的函数
{
int temp = x ;
x = y ;
y = temp ;
}
引用传递,也如前面的定义所说的那样,传递的是变量的地址。在调用Swap函数之前,实参a的值是1,地址为0x00000001,实参b的值是2,地址为0x00000002,形参x和形参y里面没有放任何值。
当调用Swap函数时,实参a把自身的地址传给形参x,实参b同理。注意,&是C++中的取地址运算符,表示取得变量的地址。
紧接着,进入Swap函数,int temp = x;就是再创建一个局部变量temp,把x的值赋值给temp,由于这是引用传递,被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量,所以,通过形参x存放的地址,找到实参a的值,把值赋值给变量temp。
同理,把变量y的值赋值给变量x。
最后再把变量temp的值赋值给变量x。
图例的结果和程序打印出来的结果一样,a=2,b=1。整个过程就是引用传递的具体实现,通过引用传递,实参把自身的地址传递给形参,然后被调函数的操作通过形参中的地址,间接对实参进行操作。
以上部分就是对值传递和引用传递的理解。
而在Java的世界里,就只有值传递。
对于基本类型的值传递,代码如下,具体实现跟C++类似。
public static void main(String[] args){
int a = 1,b = 2;
System.out.println("a="+a+" , b="+b);
swap(a,b);
System.out.println("a="+a+" , b="+b);
}
public static void swap(int x,int y){
int temp = x;
x = y;
y = temp;
}
//控制台输出的结果
a=1 , b=2
a=1 , b=2
对于对象的值传递,代码如下。
//Person类
public 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;
}
public String toString(){
return this.name+":"+this.age;
}
}
//测试
public class test {
public static void main(String[] args){
Person person = new Person("cjt",23);
System.out.println(person);
modify(person);
System.out.println(person);
}
public static void modify(Person p){
p.setName("chenjiatao");
p.setAge(18);
}
}
//控制台输出的结果
cjt:23
chenjiatao:18
看到对象值传递的结果,可能会让你想到前面C++说的引导传递,会发出疑问:不是值传递吗?怎么结果变了,这应该是引用传递吧?在这里,我这声明一次,在Java的世界里,只有值传递!
对于对象的值传递,具体看下面的解释:
上面的这张图示中展现的是Person person = new Person(“cjt”,23)这行代码具体过程,我们可以知道,我们创建了一个person对象,里面的属性是name为cjt,age为23,对象的堆上的地址是0x00000001。并且,声明实参变量person,把person对象的引用赋值给变量person,所以,如图所示,实参person里面存放的值,是指向实际person对象的引用。
紧接着,调用modify方法,实参person把自身的值拷贝一份,然后把拷贝的副本传递给形参p,此时,形参p的值也是0x00000001。注意,这里完全和前面C++部分讲的值传递一模一样,实参p把自身的副本传递给形参p之后,就没实参p什么事了,modify函数内部执行的任何代码,都不会影响到实参person里面的值,即实参person里面一直存放的0x00000001。然后执行modify函数内部的方法,由于形参p的值是同实参person一样,也是指向person对象的引用,所以modify函数内部执行的代码,改变了person对象里面的属性。当然,有的人可能会说,这个也有点类似之前C++部分讲的引用传递呀。这里要注意了,我们的关注点是实参到形参的传递过程,而且,如果说这个对象的传递过程是引用传递的话,那实参person传递给形参p的值,应该是实参person这个变量的地址,而不是person对象的地址,再说,在Java的世界里,并没有C++那样的取地址运算符&,你见到过有人在Java中用过&person这样的表达式吗?所以,正如程序所运行的结果那样,person对象的里面的属性变成了name:chenjiatao,age:18。