zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Java 中对象传入方法内赋值后,为何执行完方法后对象的值未改变呢?

JAVA方法对象执行 改变 为何 赋值 传入
2023-06-13 09:17:27 时间

问题背景:

昨天测试同事写的接口时候,发现了这样一个问题

上图中 第一步: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中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。