首先声明,Java函数传参的唯一方式是值传递,没有引用传递。(查了一下,C++中有值传递,引用传递,指针传递)
误解:基础数据类型是值传递,引用类型是引用传递
我将从函数调用栈的角度,解释为啥是值传递,值传递到底怎么传的。
示例代码如下:
package tech.chnjing;
public class PassByValueDemo {
static void increaseInt(int i) {
i = i + 1;
}
public static void main(String[] args) {
int i = 0;
increaseInt(i);
System.out.println("i 现在的值:" + i);
}
}
输出结果是
i 现在的值:0
在increaseInt函数里面随便你怎么改形参i的值,不会影响外面的实参i的值。为什么呢?看下面的函数调用过程。
- 可以看到Main函数中的变量i作为一个原始数据类型int,它的值0是直接分配在栈上的。
- 调用increaseInt函数时,会在栈中新建一个栈帧,并在该栈帧的局部变量表中新建一个变量i,将Main栈帧中i的值复制一份给到increaseInt栈帧中的变量i。
- 这就是我们说的值传递,把参数的值复制一份传递过去。
- 然后执行
i = i + 1,此时影响的是increaseInt栈帧中的i的值,变为了1。 - increaseInt执行结束,弹出increaseInt栈帧,然后回到Main函数继续执行。我们可以发现Main栈帧在increaseInt执行期间根本没受任何影响。其中的变量i仍然是0。
接下来看看引用类型参数怎么传递的,
package tech.chnjing;
public class PassByValueDemo {
static class User {
public User(int age) {
this.age = age;
}
public int age = 20;
}
static void changeUserAge(User user) {
user.age = user.age + 10;
}
static void changeUser(User user) {
user = new User(50); //新user.age为50
}
public static void main(String[] args) {
User user = new User(30);
changeUser(user);
System.out.println("user.age 现在的值:" + user.age);
changeUserAge(user);
System.out.println("changeUserAge 之后 user.age 的值:" + user.age);
}
}
输出结果:
changeUser 之后 user.age 的值:30
changeUserAge 之后 user.age 的值:40
由结果可知,user所指向的对象并没有被changeUser所改变。说明changeUser中,让形参user指向另一个新User对象,并不会让外面的实参user也指向这个新User对象,而changeUserAge成功改变了user的age属性。
我们先来看changeUser的调用过程。
-
Main中新建了一个User对象,变量user的值为User对象在堆中的地址。
-
进入
changeUser函数,创建了一个新的栈帧并在其局部变量表里新建了一个变量user,复制Main栈帧中user变量的值给到changeUser栈帧中的user变量,也就是0xfff0,为User对象在堆中的地址。 -
执行
user = new User(50);, 此时在changUser栈帧里面的user变量为对象User2的地址,0xffff -
随着
changeUser执行结束,changeUser栈帧弹出,回到Main函数,此时Main栈帧中的user变量的值仍然为0xfff0,指向User对象,没有受到任何影响。
可以看出,在changeUser函数中,参数为引用类型,但是和前面例子里的int类型参数一样,仍然是复制了一份user变量的值到新的栈帧里。只不过前面例子中复制的值为0,后面例子中复制的值为0xfff0。
因此我们说Java里面只有值传递,不管你是原始类型还是引用类型。
接下来看changeUserAge的调用过程。
-
在执行
user.age = user.age + 10;时,通过user变量中的地址0xfff0,找到User对象,将其age属性改为40; -
执行完
changeUserAge后,changeUser栈帧弹出,回到Main函数,此时user变量指向的User对象已经被修改过了。
猜想:如果需要,Java怎样实现引用传递来传递参数?
在进入函数调用,创建栈帧时,形参的值设为实参的地址(现在是实参的值)。访问形参的值就会通过实参的地址找到实参,取回实参的值。修改形参指向的对象,就会改变实参指向的对象。