Java 到底是值传递还是引用传递?

作者:Hollis
关于这个问题,引发过很多广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同,甚至很多人理解的是错误的 。还有的人可能知道JAVA中的参数传递是值传递,但是说不出来为什么 。
在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了 。如果你有以下想法,那么你有必要好好阅读本文 。

错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递 。如果是个引用,就是引用传递 。
错误理解二:Java是引用传递 。
错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递 。
实参与形参我们都知道,在Java中定义方法的时候是可以定义参数的 。比如Java中的main方法,public static void main(String[] args),这里面的args就是参数 。参数在程序语言中分为形式参数和实际参数 。
形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数 。
实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系 。在主调函数中调用一个函数时,函数名后面括号中的参数称为“实际参数” 。
简单举个例子:
public static void main(String[] args) {ParamTest pt = new ParamTest();pt.sout("Hollis");//实际参数为 Hollis}public void sout(String name) { //形式参数为 nameSystem.out.println(name);}实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数 。
值传递与引用传递上面提到了,当我们调用一个有参函数的时候,会把实际参数传递给形式参数 。但是,在程序语言中,这个传递过程中传递的两种情况,即值传递和引用传递 。我们来看下程序语言中是如何定义和区分值传递和引用传递的 。
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数 。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数 。
有了上面的概念,然后大家就可以写代码实践了,来看看Java中到底是值传递还是引用传递,于是,最简单的一段代码出来了:
public static void main(String[] args) {ParamTest pt = new ParamTest();int i = 10;pt.pass(10);System.out.println("print in main , i is " + i);}public void pass(int j) {j = 20;System.out.println("print in pass , j is " + j);}上面的代码中,我们在pass方法中修改了参数j的值,然后分别在pass方法和main方法中打印参数的值 。输出结果如下:
print in pass , j is 20print in main , i is 10可见,pass方法内部对name的值的修改并没有改变实际参数i的值 。那么,按照上面的定义,有人得到结论:Java的方法传递是值传递 。
但是,很快就有人提出质疑了(哈哈,所以,不要轻易下结论咯 。) 。然后,他们会搬出以下代码:
public static void main(String[] args) {ParamTest pt = new ParamTest();User hollis = new User();hollis.setName("Hollis");hollis.setGender("Male");pt.pass(hollis);System.out.println("print in main , user is " + hollis);}public void pass(User user) {user.setName("hollischuang");System.out.println("print in pass , user is " + user);}同样是一个pass方法,同样是在pass方法内修改参数的值 。输出结果如下:
print in pass , user is User{name='hollischuang', gender='Male'}print in main , user is User{name='hollischuang', gender='Male'}经过pass方法执行后,实参的值竟然被改变了,那按照上面的引用传递的定义,实际参数的值被改变了,这不就是引用传递了么 。于是,根据上面的两段代码,有人得出一个新的结论:Java的方法中,在传递普通类型的时候是值传递,在传递对象类型的时候是引用传递 。
但是,这种表述仍然是错误的 。不信你看下面这个参数类型为对象的参数传递:
public static void main(String[] args) {ParamTest pt = new ParamTest();String name = "Hollis";pt.pass(name);System.out.println("print in main , name is " + name);}public void pass(String name) {name = "hollischuang";System.out.println("print in pass , name is " + name);}上面的代码输出结果为
print in pass , name is hollischuangprint in main , name is Hollis


推荐阅读