Java中的引用类型真的是引用传递么?

目录

1.辟谣时间

2.什么是求值策略

    2.1 严格求值

    2.2 Java中的求值策略

3.总结


1.辟谣时间

       在开始深入讲解之前,有必要纠正一下大家以前的那些错误看法了。如果你有以下想法,那么你有必要好好阅读本文:

    (1)错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。

    (2)错误理解二:Java是引用传递。

    (3)错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。

2.什么是求值策略

       每当我们进行方法调用的时候,需要把实际参数传递给形式参数,那么传递的过程中到底传递的是什么呢?这其实是程序设计中求值策略的概念。在计算机科学中,求值策略是确定编程语言中表达式的求值的一组(通常确定性的)规则。求值策略定义何时和以何种顺序求值给函数的实际参数、什么时候把它们代换入函数、和代换以何种形式发生。求值策略分为两大基本类,基于如何处理给函数的实际参数,分为严格的和非严格的。

    2.1 严格求值

       在“严格求值”中,函数调用过程中,给函数的实际参数总是在应用这个函数之前求值。多数现存编程语言对函数都使用严格求值。所以,我们本文只关注严格求值。在严格求值中有几个关键的求值策略是我们比较关心的,那就是传值调用传引用调用以及传共享对象调用

       传值调用(值传递):在传值调用中,实际参数先被求值,然后其值通过复制,被传递给被调函数的形式参数。因为形式参数拿到的只是一个"局部拷贝",所以如果在被调函数中改变了形式参数的值,并不会改变实际参数的值。

       传引用调用(引用传递):在传引用调用中,传递给函数的是它的实际参数的隐式引用而不是实参的拷贝。因为传递的是引用,所以,如果在被调函数中改变了形式参数的值,改变对于调用者来说是可见的。

       传共享对象调用(共享对象传递):传共享对象调用中,先获取到实际参数的地址,然后将其复制,并把该地址的拷贝传递给被调函数的形式参数。因为参数的地址都指向同一个对象,所以我们也称之为"传共享对象",所以如果在被调函数中改变了形式参数的值,调用者是可以看到这种变化的。

       不知道大家有没有发现,其实传共享对象调用和传值调用的过程几乎是一样的,都是进行"求值"、"拷贝"、"传递"。并且传共享对象调用和传引用调用的结果又是一样的,都是在被调函数中如果改变参数的内容,那么这种改变也会对调用者有影响。那么,共享对象传递和值传递以及引用传递之间到底有很么关系呢?

       对于这个问题,我们应该关注过程,而不是结果,因为传共享对象调用的过程和传值调用的过程是一样的,而且都有一步关键的操作,那就是"复制",所以通常我们认为传共享对象调用是传值调用的特例我们先把传共享对象调用放在一边,我们再来回顾下传值调用和传引用调用的主要区别:传值调用是指在调用函数时将实际参数复制一份传递到函数中,传引用调用是指在调用函数时将实际参数的引用直接传递到函数中。所以,传值调用和传引用调用最主要区别就是传值过程中是直接传递的,还是传递的是一个副本。

    2.2 Java中的求值策略

       前面我们介绍过了传值调用、传引用调用以及传值调用的特例传共享对象调用,那么,Java中是采用的哪种求值策略呢?很多人误认为Java中的对象传递是引用传递,之所以会有这个误区,主要是因为Java中的变量和对象之间是有引用关系,Java语言中是通过对象的引用来操纵对象的。所以很多人会认为对象的传递是引用的传递。这真的正确么?我们前面说过,无论是值传递,还是引用传递,只不过是求值策略的一种,那求值策略还有很多,比如前面提到的共享对象传递的现象和引用传递也是一样的。那凭什么就说Java中的参数传递就一定是引用传递而不是共享对象传递呢?Java中的对象传递,到底是哪种形式呢?其实,还真的就是共享对象传递。其实在 《The Java™ Tutorials》中,是有关于这部分内容的说明的。首先是关于基本类型描述如下:

       Primitive arguments, such as an int or a double, are passed into methods by value. This means that any changes to the values of the parameters exist only within the scope of the method. When the method returns, the parameters are gone and any changes to them are lost.

       即,原始参数通过值传递给方法。这意味着对参数值的任何更改都只存在于方法的范围内。当方法返回时,参数将消失,对它们的任何更改都将丢失。关于对象传递的描述如下:

       Reference data type parameters, such as objects, are also passed into methods by value. This means that when the method returns, the passed-in reference still references the same object as before. However, the values of the object’s fields can be changed in the method, if they have the proper access level.

       也就是说,引用数据类型参数(如对象)也按值传递给方法。这意味着,当方法返回时,传入的引用仍然引用与以前相同的对象。但是,如果对象字段具有适当的访问级别,则可以在方法中更改这些字段的值。这一点官方文档已经很明确的指出了,Java就是值传递,只不过是把对象的引用当做值传递给方法。其实Java中使用的求值策略就是传共享对象调用,也就是说,Java会将对象的地址的拷贝传递给被调函数的形式参数。只不过"传共享对象调用"这个词并不常用,所以Java社区的人通常说"Java是传值调用",这么说也没错,因为传共享对象调用其实是传值调用的一个特例。

3.总结

       我们知道,编程语言中需要进行方法间的参数传递,这个传递的策略叫做求值策略。在程序设计中,求值策略有很多种,比较常见的就是值传递和引用传递。还有一种值传递的特例——共享对象传递。值传递和引用传递最大的区别是传递的过程中有没有复制出一个副本来,如果是传递副本,那就是值传递,否则就是引用传递。在Java中,其实是通过值传递实现的参数传递,只不过对于Java对象的传递,传递的内容是对象的引用。我们可以总结说,Java中的求值策略是共享对象传递,这是完全正确的。但是,为了让大家都能理解你说的,我们说Java中只有值传递,只不过传递的内容是对象的引用。这也是没毛病的。但是,绝对不能认为Java中有引用传递。

      本篇大量内容借鉴自:http://hollischuang.gitee.io/tobetopjavaer/#/basics/object-oriented/why-pass-by-reference写的太好了强推大家去看看

猜你喜欢

转载自blog.csdn.net/qq_36756682/article/details/111641674