在计算机科学中,参数传递的形式主要有以下2种: 值调用和引用调用,为了说明Java在传参过程中的参数传递方式,我们首先需要对上述中2种调用形式的定义做清晰的介绍
定义
现给出两者的定义:
值调用(Call by Value): 指函数接收的是调用者提供的实参变量的值
引用调用(Call by reference): 指函数接收的是调用者提供的实参变量的地址
两种传递方式名词中的“值”和“引用”,是对函数调用时对实参求值策略的分类,而不是指实参的类型。所以,不能依据传入的实参类型来判断是何种调用方式。常见的理解错误,认为Java中的实参为基本数据类型即值调用,实参为引用类型即为引用调用。实际上,在Java中只有值调用
两种求值策略区别如下:
值调用: 被调函数的形参得到的是实参变量的值的副本,无法在被调函数中去修改实参的值
引用调用: 被调函数的形参得到的是实参变量本身,故可以通过被调函数中修改实参变量的值
Java 只有值调用
具体到Java中,其函数调用时的参数传递,是值调用还是引用调用?我们将分基本类型和引用类型分别讨论
- 基本数据类型
对于基本数据类型(byte, short, int, long, float, double, char, boolean)而言(也称为值类型),形参得到是将实参变量的值的副本,显然被调函数中对形参的修改无法更新作用于实参,所以,对于实参为基础类型的情况而言,其参数调用类型为值调用
- 引用类型
对于实参为引用类型时,根据上文可知,我们不能直接根据实参类型来断定其是引用调用
在Java中引用类型的变量的值,实际上存放的是所指对象的地址,其形参的得到也即该值(实参所指对象的地址)的拷贝,由于此时形参引用和实参引用都是指向同一对象,则在被调函数通过形参的引用变量修改所指对象的数据时,实参的引用同样可以获得对所指对象的相应修改。故很多人据此错误得出其为引用调用。其实这是错误的,因为正如上文所言,形参做的修改之所以能被实参所使用,是因为两者均指向同一个对象,其传递的依然是引用变量的值(即,对象地址),而不是传递引用变量的地址。所以如果在被调函数中,修改形参的引用变量的值,即改变形参引用变量其所指向的对象,其结果并不会改变实参的引用变量所指向的对象。
综上所述,对于实参为引用类型的情况而言,其参数调用类型同样为值调用,只不过其值是对象的地址
Note
- 不可变对象
不可变对象,即对象一旦被创建后它的状态(对象的属性数据)就不能被改变。以下类的实例为不可变的:String, 基本类型的包装器, BigInteger和BigDecimal等
将不可变对象作为实参传入方法后,实参并不会被方法中的操作所修改,因为其为不可变对象,在方法中,欲改变该对象的操作实际会生成一个新对象,并让形参指向该新对象。所以,如前文所述,形参引用变量指向其他对象,并不会影响实参的指向
- 数组
Java中,数组类型为对象。故可按照实参为引用类型进行考虑分析。即,将数组作为形参传入方法后,在方法中对数组元素的修改,该修改可以体现在原实参数组中。但是如果在方法中,修改形参引用,使其指向其他数组对象,则不会影响原实参数组的内容