Java参数传递

Java参数传递是值传递还是引用传递?

结论:基本类型的传递是值传递,其他对象的传递是引用传递

1.对于基本类型以及对应的包装类、String

public class StringRefTest {

    void dealWithString(String str){
        str = "123";
        System.out.println(str);
    }

    public static void main(String[] args) {
        StringRefTest stringRefTest = new StringRefTest();
        String str = "456";
        stringRefTest.dealWithString(str);
        System.out.println(str);
    }

}

输出结果:

123
456

利用debug工具发现
当str值为123时,内部value的值为byte[3]@497
当str值为456时,内部value的值为byte[3]@500
str值与value数组的关系在函数内外都保持一致

我们知道String内部的value是str值的表现,而value是final类型的数组,创建后不可修改;所以在函数内重新赋值后,str变量是另一个String对象,内部的value数组也是不同的值,不同的引用地址。
但是函数外的String不变,因为函数内的形参只是对原本String的拷贝。

其他的基本类型、包装类同理,作为函数形参传入后修改形参变量的值,不会影响到函数外该实参的值。常量是不可变的。

如果函数传入的值其他对象呢?
2.对于其他对象

public class ObjectRefTest {

     void test(Obj a){
        Obj b = new Obj();
        a = b;
        System.out.println("b:" + b);
        System.out.println("a:" + a);
    }

    public static void main(String[] args){
        Obj a = new Obj();
        System.out.println("before change, a:" + a);
        ObjectRefTest objectRefTest = new ObjectRefTest();
        objectRefTest.test(a);
        System.out.println("after change, a:" + a);
    }

}

class Obj {
}

输出结果:

before change, a:reference.Obj@506e1b77
b:reference.Obj@4fca772d
a:reference.Obj@4fca772d
after change, a:reference.Obj@506e1b77

函数内a的地址等价于4fca772d,但是函数外a的地址并未发生改变
上例说明,其他对象在函数内被修改引用关系,函数外引用关系不变

public class ObjectRefTest {

     void test(Obj a){
        a.i = 2;
        System.out.println("a.i:" + a.i);
    }

    public static void main(String[] args){
        Obj a = new Obj(1);
        System.out.println("before change, a.i:" + a.i);
        ObjectRefTest objectRefTest = new ObjectRefTest();
        objectRefTest.test(a);
        System.out.println("after change, a.i:" + a.i);
    }

}

class Obj {
    int i;
    Obj(int i){this.i = i;}
}

输出结果:

before change, a.i:1
a.i:2
after change, a.i:2    

但是在函数体内修改传入的其他对象的值,函数外该值也随之变化

为什么会发生以上两种不同的结果?
因为在第二个例子中,a是对创建的i为1的对象的引用,可以称该对象为真实对象;在函数体内修改i值的对应对象就是真实对象,对a的修改即是对真实对象的修改,a所引用的对象直接指向了堆中对象;在Java是引用传递还是值传递的问题中,所谓的”引用传递“就是指这种情况;
而在第一个例子中,按上文理解,存在真实对象a,真实对象b;a = b 这句指令是将a的引用指向了b所对应的引用,所以函数体内a的对象地址和b的对象地址相同,该引用a只是栈内变量的修改;而真实对象a并未发生任何修改,函数体外a的引用保持不变;

需要注意的是
引用:
Java中一句 A a0 = new A(); 创建了一个对象A,以及对该对象的引用a0;
A a1 = a; 创建了引用a1,a1引用的目标是对象A

 void test(Obj a){
        Obj a1 = a;
        Obj a2 = a1;
        Obj a3 = a2;
        a2 = null;
        a.i = 2;
        System.out.println("a.i:" + a.i);
        System.out.println("a3.i:" + a3.i);
    }

输出结果:

a.i:2
a3.i:2

根据上例,如果a3的引用是a2,那么a3也会随着a2赋值为null而被修改为null;但是实际上a3依然引用了对象a;所以对引用关系的赋值实际上还是直接指向其真实对象。

堆栈:
Java的内存布局比较复杂,简言之就是注意对象实例分配在堆区;而方法内部的内存区是虚拟机栈,局部变量等都存储在该区间。

总结以上,结论是传入引用关系时,对引用关系所对应的对象的修改会作用于函数外。

题外话:有人说引用传递传递的是对象所对应的内存地址,文中”真实对象“和内存地址也是一对一的关系嘛,所以引用传递也是值传递。我认为这种咬文嚼字是没有意义的,重要的是搞清函数参数传递的过程与区别。知乎上这个讨论很有价值,https://www.zhihu.com/question/31203609?sort=created。这里面也有一些对引用传递和值传递概念上的解释,实际上我们大多数人也不太搞得清这个概念,在此之前我们先不要纠结于概念,先弄明白参数传递的过程是什么样的。

posted @ 2019-08-30 11:43  Elinlinlin  阅读(216)  评论(0编辑  收藏  举报