天天看点

Java方法参数传递的机制

作者:码世界

目前两种比较流行的方法参数传递模式主要是值传递和引用传递。不同的编程语言对于这两种机制可能有不同的处理方式。对Java来说,一切都是严格按值传递的。

接下来,我们来一起探讨下 Java 如何为各种类型传递参数。在此之前,我们先解读下值传递和引用传递的概念。

值传递 vs 引用传递

让我们先看下函数参数传递的不同机制有哪些:

  • 按值
  • 按引用
  • 按结果
  • 按值-结果
  • 按名称

不过目前流行的编程语言中用的最多的就是按值和按引用传递,下面具体看下概念:

值传递

当一个参数作为方法的值传递时,被调用的方法其实是拿到了一个传入参数值的拷贝,所以各自的参数变量的修改是互不影响的。

这意味着当你调用一个方法时,传递给被调用方法的参数将是原始参数的克隆。对于被调用方法的任何修改对调用方法中的源参数毫无影响。

引用传递

当一个参数作为方法的引用传递时,调用方法和被调用方法其实操作的是同一个对象。

具体来说就是这个对象的唯一标识将会发送给被调用方法,任何对这个参数实例中的成员进行修改都会影响原始对象的值。

Java中的参数传递

在Java中,基本数据类型变量存储的都是实际的值,但是对于非基础数据类型(比如:类)变量存储的是指向这个对象的地址引用。不管是值还是引用它们都存储在栈内存中。

对于Java的方法参数来说总是严格按照值传递方式(copy模式)来处理的。因为在方法调用的期间,不管这个参数类型是值还是引用都会拷贝一个副本并且会在栈内存中开辟一个空间,然后在传递给目标方法。

对于基础数据类型变量来说,就是简单的拷贝一个值到栈内存,然后再传递给目标方法。对于非基础数据类型变量来说,就是一个栈内存的引用并指向存放在堆中的实际对象,当我们传递这个对象时,实际传递的是这个对象对应的栈内存引用的副本。

还是以具体代码案例来说明吧:

基础数据类型变量传递

Java编程语言具有八种基本数据类型。基础数据类型会直接存储在栈内存中。任何基础类型的变量不管在什么时候作为参数传递时,实际的参数都会拷贝到形式参数中并且这些形式参数在栈内存中都有自己的独立空间。

这些形式参数的的生命周期会仅仅在该方法运行期间存在,随着方法执行return后将从堆栈中清除并丢弃。

Java方法参数传递的机制

值传递案例

Java方法参数传递的机制

值传递栈示意图

通过上图可以看出,目标方法pasbyValue中对变量i进行修改后并不影响调用方法中的变量i,栈的示意图也表明目标方法中的变量i完全是一个新的值,所以不管怎么修改都不会影响源值。

对象类型传递

在Java中,所有的对象都会存储在堆空间中。这些对象会被称为“引用变量”的引用而引用。

与基础类型存储方式不一样,Java对象会被存储在两个不同的地方。引用变量会被放在栈内存中而被引用的对象会被存储在堆内存中。

每当将对象作为参数传递时,就会创建一个引用变量的副本并指向一个堆内存的对象,这个堆内存对象就是原始引用的对象,简单来说就是两个引用同时指向同一块堆内存区域。

所以我们得出一个结论就是,每当我们对传递过来的对象进行修改时都会导致原始对象的改变。但是,如果我们对传递的引用重新赋予一个新的对象,此时原始对象是不会受影响的(引用副本指向了一块新的堆内存区域)。

下图为目标方法仅仅修改对象的属性:

Java方法参数传递的机制

引用指向同一个对象

Java方法参数传递的机制

引用指向同一个对象堆栈示意图

下图为目标方法为引用重新复制一个新的对象:

Java方法参数传递的机制

引用对象指向不同对象

Java方法参数传递的机制

引用对象指向不同对象的堆栈示意图

总结

通过以上的解释和演示,我们知道Java中的参数传递总是按值传递,对于方法执行参数结果的变化取决于传入的是基础数据类型还是对象:

  • 对于基础数据类型,参数按值传递
  • 对于对象类型,对象的引用是按值传递,但是我们要注意按值传递后的应用会不会指向新的对象,这个是值得我们关注的地方

继续阅读