Java 到底是值传递还是引用传递?( 二 )

这又作何解释呢?同样传递了一个对象,但是原始参数的值并没有被修改,难道传递对象又变成值传递了?
Java中的值传递上面,我们举了三个例子,表现的结果却不一样,这也是导致很多初学者,甚至很多高级程序员对于Java的传递类型有困惑的原因 。
其实,我想告诉大家的是,上面的概念没有错,只是代码的例子有问题 。来,我再来给大家画一下概念中的重点,然后再举几个真正恰当的例子 。

值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数 。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数 。
那么,我来给大家总结一下,值传递和引用传递之前的区别的重点是什么 。
 
Java 到底是值传递还是引用传递?

文章插图
 
 
我们上面看过的几个pass的例子中,都只关注了实际参数内容是否有改变 。如传递的是User对象,我们试着改变他的name属性的值,然后检查是否有改变 。其实,在实验方法上就错了,当然得到的结论也就有问题了 。
为什么说实验方法错了呢?这里我们来举一个形象的例子 。再来深入理解一下值传递和引用传递,然后你就知道为啥错了 。
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递 。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字 。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递 。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙 。
但是,不管上面那种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了 。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么 。
还拿上面的一个例子来举例,我们真正的改变参数,看看会发生什么?
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 = new User();user.setName("hollischuang");user.setGender("Male");System.out.println("print in pass , user is " + user);}【Java 到底是值传递还是引用传递?】上面的代码中,我们在pass方法中,改变了user对象,输出结果如下:
print in pass , user is User{name='hollischuang', gender='Male'}print in main , user is User{name='Hollis', gender='Male'}我们来画一张图,看一下整个过程中发生了什么,然后我再告诉你,为啥Java中只有值传递 。
 
Java 到底是值传递还是引用传递?

文章插图
 
 
稍微解释下这张图,当我们在main中创建一个User对象的时候,在堆中开辟一块内存,其中保存了name和gender等数据 。然后hollis持有该内存的地址0x123456(图1) 。当尝试调用pass方法,并且hollis作为实际参数传递给形式参数user的时候,会把这个地址0x123456交给user,这时,user也指向了这个地址(图2) 。然后在pass方法内对参数进行修改的时候,即user = new User();,会重新开辟一块0X456789的内存,赋值给user 。后面对user的任何修改都不会改变内存0X123456的内容(图3) 。
上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user=new User()的时候,实际参数的引用也应该改为指向0X456789,但是实际上并没有 。
通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数 。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数 。
我们再来回顾下之前的那个“砸电视”的例子,看那个例子中的传递过程发生了什么 。
 
Java 到底是值传递还是引用传递?

文章插图
 
 
同样的,在参数传递的过程中,实际参数的地址0X1213456被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容 。


推荐阅读