突然想到这个问题,不是很确定,然后查了一下资料,做一下总结.
1.什么是值传递,什么是引用传递
值传递是将需要传递的值作为一个副本传递出去,例如:
int i = 5;
int j = i;
这里把i的值5拷贝了一份副本,作为j的值,结果是i = 5,j = 5.如果改变j的值,也就是拷贝出来的副本的值,
这个改变和原来的值毫无关系,因此改变j的值不会对i的值产生影响.
引用传递,是将引用对象的地址传递出去,例如:
int *p,*p1;
int i = 5; *p = i; //这里将i在内存中的地址赋与了指针p
p1 = p; //将指针p中的地址(即i的内存地址)赋与p1
此时,改变i的值,
i = 10;
由于指针p,p1指向的地址都是i的地址,因此*p 和 *p1 的值都是10.
2.Java中函数参数的传递形式
java中的数据分为基本数据类型和引用数据类型,我们来对这两种类型进行分析.
对于基本数据类型来说,举个例子:
int i = 10;
add(i);
void add(int temp){
temp +=5;
}
这个很简单,我们都知道调用函数时将i的值10拷贝给函数的参数了,函数调用结束后i的值并没有发生变化,这是很明显的值传递过程,我们来看看在内存中的变化过程.
首先在栈内存中为基本数据类型变量i分配内存空间并初始化值为10,然后开始调用函数add().执行add函数的时候,为temp变量分配栈内存空间,并将i的值拷贝给temp,在add执行之后,temp的值增加了5,随着add函数执行完毕,temp变量的生命周期结束了.可见,这个temp变量和i变量没有关系,temp变量可以随意命名,将其命名为i,也和之前申明的变量i毫无关系.
我找来一幅图,可以看看,在函数运行的时候,只是临时分配产生了一个值与i相同的另外一个变量而已,一旦完成了值得拷贝工作,之后这两个变量就没有任何联系了.因此,对这个临时变量的操作不会对i产生任何影响.
对于引用数据类型参数来说,举个例子:
class Person {
String name = "java";
public void setName(String name){
this.name = name;
}
}
void changeName (Person p1){
p1.setName("javascript");
}
public static void main (String[] args){
Person p = new Person();
changeName(p);
}
我们来看看这一过程中内存中发生的变化:
首先在堆内存中创建了一个Person对象,然后在栈内存中创建了一个引用类型的变量p,p指向堆内存中的Person对象,即p的值为Person对象在内存中分配空间的首地址值.
调用changeName()函数时,栈中再次创建一个引用类型的变量p1,然后将p中的值拷贝给p1,即p1也指向Person对象. 调用完成后,p1指向的Person对象的name属性被改变为javascript.由于p和p1指向同一个Person对象,因此p指向的Person对象的name值也变为javascript.
这看起来类似于引用传递,但是这仍然是值传递,p1接受的值是p的值的拷贝,函数调用完成后,p的值完全没有改变,仍然是指向之前创建的Person对象,这就是值传递.
整个过程中改变的是Person对象的属性name,由于p和p1都指向这个Person对象,因此p和p1产生了相同的变化,使得看起来像是进行了引用传递.
如果是引用传递的话,那么p1接收的值将会是p的地址,那么p1将不会再指向Person对象,整个程序就会产生错误.
可见,引用数据类型的参数仍然是进行值传递的,只不过这里传递的值是对象在内存中的首地址,在java中即为指向该对象的引用.而引用传递是直接将变量的地址传递给参数,很明显,这样参数接收到的值将不再指向特定的对象,整个程序将会产生错误,要传递地址的话,应该是传递Person对象的地址而不是p对象的地址.
同样用图简单概括一下
概括一下,java中函数参数的传递方式是值传递,不管参数是基本数据类型还是引用数据类型.