问题背景:
昨天测试同事写的接口时候,发现了这样一个问题
上图中 第一步:lhygTaskMode = null ,紧接着传入 checkParam 方法内部,进行赋值。最后方法执行完毕后,lhygTaskMode 依然为 null,这是为什么呢?
问题原因:
因为java只有一种传递参数的方式:值传递。
在值传递中,实参的值被传给形参,方法体内对形参的任何赋值操作都不会影响到实参。
测试用例:
接下来我们简单写个测试用例来看看具体输出。
public class Test {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
Test a = null;
changeObj(a);
System.out.println(a);
}
public static void changeObj(Test a) {
a = new Test();
System.out.println("changeObj>>"+a);
}
}
执行上面的代码输出结果如下:
可以看到方法内部对象赋值后,是有对象地址输出的,但是方法执行完毕后,源对象 a 依然是 null。
接下来我们再测试一下,修改源对象属性值后的输出结果。
package com.btonline365.support.test;
public class Test {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
// Test a = null;
// changeObj(a);
// System.out.println(a);
Test a = new Test();
a.setName("王五");
changeObjValue(a);
System.out.println(a.name);
}
public static void changeObj(Test a) {
a = new Test();
System.out.println("changeObj>>"+a);
}
public static void changeObjValue(Test a) {
a.setName("李四");
System.out.println("changeObjValue>>"+a.name);
}
}
输出如下:
这里属性值由王五变成了李四。
那第二次的测试,修改了形参的属性值,为什么形参就会把实参属性值也顺便修改了呢?
对形参的任何赋值操作都不会影响到实参,但是对于形参的字段,或者元素(假如形参是一个数组)的赋值操作会影响实参。
补充说明:
值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
那么,我来给大家总结一下,值传递和引用传递之前的区别的重点是什么。
值传递 |
引用传递 |
|
根本区别 |
会创建副本 |
不创建副本 |
所有 |
函数中无法改变原始对象 |
函数中可以改变原始对象 |
场景说明:
如果你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
但是,不管上面那种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?
public static void main(String[] args) {
Test a1 = new Test();
System.out.println("源对象初始地址:"+a1);
changeObj(a1);
System.out.println(a1);
}
public static void changeObj(Test a) {
a = new Test();
System.out.println("changeObj>>"+a);
}
上面可以看到对象的地址值并没有发生变化。
稍微解释下这张图,当我们在main中创建一个Test对象的时候,在堆中开辟一块内存。然后a1持有该内存的地址 @15db9742 (图1)。当尝试调用changeObj方法,并且a1作为实际参数传递给形式参数a的时候,会把这个地址@15db9742 交给a,这时,a也指向了这个地址(图2)。然后在changeObj方法内对参数进行修改的时候,即a=new Test();,会重新开辟一块 @6d06d69c的内存,赋值给a。后面对a的任何修改都不会改变内存@15db974的内容(图3)。
上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在a=new Test()的时候,实际参数的引用也应该改为指向@15db974,但是实际上并没有。
通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。
所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。就像钥匙和房子的关系。
所以说,Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。