初学Java不少同学都对方法参数是按值调用还是按引用调用模棱两可,网上帖子也是众说纷纭,部分文章干脆采取中庸之道,认为两者兼而有之,怕是止增笑耳。下面笔者谈谈自己的见解,来证明一下Java为什么只有值调用而没有引用调用。
首先我们重温一下什么是值调用,值调用表示方法接收的是调用者提供的值(注意这个“值”不仅仅是数值);那什么是按引用调用呢?它表示方法接收的是调用者提供的变量地址。说白了调用者和被调用者的地址一样了,这两者就是同一变量了,一个变了另一个一定变,唇亡齿寒的关系,也就是对形参所做的任何改变都会反映在实参上。
有了前期的铺垫我们直接来看代码:
package summer;
public class ParamTest {
public static void swap (Employee x,Employee y)
{
Employee temp=x;
x=y;
y=temp;
System.out.println("完事儿之后x="+x.getName());
System.out.println("完事儿之后y="+y.getName());
}
public static void main(String[] args)
{
Employee a=new Employee("liu",200);
Employee b=new Employee("sun",3000);
System.out.println("before a="+a.getName());
System.out.println("before b="+b.getName());
ParamTest.swap(a,b);
System.out.println("after a="+a.getName());
System.out.println("after b="+b.getName());
}}
大家非常熟悉的交换值函数,public static void swap (Employee x,Employee y)在这一行中,形参x和实参a指向同一对象1;形参y和实参b指向同一对象2。如果Java在按引用调用的话x的地址和a的地址就一样了,也就是x和a是同一变量(虽然形参和实参是一个地址,是同一个变量这在java里本就不可能) 因此x,y在交换完事儿之后,a和b的值也是交换完的。是这样的吗?我们看看结果:
事与愿违,形参x,y完成交换而实参a,b无动于衷。所以按引用传递是站不住脚的,那么笔者所说的按值传递,在这个问题中“值”指的什么呢?拿实参a和形参x来讲,这里传递的值是a的值也就是引用的值,就是(引用)a指向的那个对象的地址!注意是a指向的那个对象的地址,不是引用a自身的地址。a不是对象,它是引用,它的值是对象的地址。它自身也有地址的。
如果说a自身的地址传给x了,那叫按引用调用。事实并非如此。
看到这里读者会问,那这个例子怎么体现的形参的“值”变了,实参的“值”没变呢?且看这两行
x=y;
y=temp;
引用之间复制了说明什么?说明new了!说明引用指向的对象变了!我们来看这张图
引用x指向的对象改了,说明x的值改了,改为对象2的地址了。正因为是按值调用,a的值没有发生变化,仍是对象1的地址,a仍指向对象1。至此相信大家已经信服了Java只有按值传递。
下面再举一个易混淆的例子:
package summer;
public class ParamTest {
public static void tripleSalary(Employee y) {
y.raiseSalary(200); //静态方法不能直接调用非静态方法,要通过对象调用(实例化)
System.out.println("end of method:salary="+y.getSalary());
}
public static void main(String[] args)
{Employee harry =new Employee("tao",5000);
System.out.println("before:salary="+harry.getSalary());
ParamTest.tripleSalary(harry);
System.out.println("after:salary="+harry.getSalary());
}}
我们观察到这里实参随着形参变了,难道按引用传递了?非也,还是按值传递,这里的值是什么呀?当然还是两个引用所指的那个对象的地址,形参y和实参harry一直还是指向的同一个对象啊,形参方法中有new吗?指向的对象变了吗?所以形参的值变了吗?恐怕都没有吧!