Java 中有一个很经典但是一直争论不休的问题。就是 Java 在传参时到底是传递值还是传递引用。
在讨论到底是值传递还是引用传递之前,先来准确定义一下值和引用。
值是指变量存储的就是值本身,比如 Java 的基本类型。 引用是指变量存储的是指向实际值的引用,也称之为句柄,比如 Java 中的数组和对象,来看下图:
要想完整的理解这个点,需要稍微提一下 JVM 的知识。我们都知道 Java 是运行在 Java 虚拟机(JVM)上的。JVM 中有两块著名的内存区域:Java虚拟机栈和堆。值类型的变量所占据的内存是在栈上分配,引用类型的变量所占据的内存是在堆上分配。
这块展开说就会涉及到 JVM 的内存模型,后续通过其他的文章再进行说明。
赋值操作
先看一下这段代码:
对于上面代码的执行结果,应该很简单:
赋值操作其实就是新建了一个变量,把原变量中的值拷贝了一份到新变量中。如下图:
也就是说修改 b 其实就是修改 b 变量中的值,与 a 变量完全没有关系。因为在把 a 的值赋值给 b 时,实际上是把 a 变量的值拷贝了一份给 b。拷贝完之后 a 和 b 就没有关系了。
其实这里的说法不完全正确,与 JVM 的机制有关,但是大体上可以这样理解。
再来看一个例子:
与上面的例子不同,这个例子的输出是:
上面的例子执行的情况如下图:
在将 paramA 的值赋给 paramB 时,和上面的基本类型其实是一样,把 paramA 中的引用创建了一个副本,所以 paramB 中存储的也是指向同一个对象的引用。所以通过 paramB 也就可以修改对象。
再看下面这个例子:
这个例子输出的结果又不同:
这个例子的执行情况是这样的:
paramB 再进行 new 操作之后,paramB 中存储的引用就变成了新对象的地址了。所以修改新的对象并不会影响原来的对象。
所以可以把 Java 中的赋值操作总结为:对原变量中存储的内容进行拷贝,把拷贝的内容放到新的变量中,不论变量中存储的是基本类型还是引用类型
Java 中的参数传递
定义一个方法:
方法参数中的 a 称之为形参,在调用时传入的参数称之为实参。
先说一下结论,Java 中参数的传递其实和赋值操作是一样的,相当于把实参赋值给形参。
所以传参也是将实参的副本给形参,修改形参的内容并不会影响实参(注意:这里说的是修改形参,并不是修改形参引用的对象)。
通过下面例子来证实一下:
例子的输出为:
这个结果与上面基本类型的变量进行赋值的结果一直。再来看看引用类型参数的传递:
这个例子的输出为:
这个例子与上面第二个例子的效果一致。再来看最后一个例子:
最后这个例子的输出为:
也与上面第三个例子的结果表现一致。所以在 Java 中参数传递的方式与赋值完全一样。都是拷贝原变量中的内容,这种方式就是值传递。如果在传递的过程中不是通过副本,而是直接传递变量本身,这种方式称之为引用传递。
所以说 Java 中的参数传递是值传递。