对于Java的初学者来说,要想把这个问题回答正确,是比较难的。因为说到值传递就必须和另外一门编程语言C或者C++引用传递来对比说明,所以才有了我写这篇文章的初衷,通过这篇文章从什么是方法的实际参数和形式参数开始,我们来彻底搞明白Java中为什么只有值传递!
1.实参和形参
先说结论:方法定义处的参数是形参,方法调用处的参数是实参。
看下面代码:
public class Demo1 {
public static void swap(int num1, int num2) {
System.out.println("=======in swap()=======");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
num1 = 100;
num2 = 200;
System.out.println("after change....");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("======in main after call swap========");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
}
运行结果:
=======in swap()=======
num1 = 10
num2 = 20
after change....
num1 = 100
num2 = 200
======in main after call swap========
num1 = 10
num2 = 20
我们发现,主方法里的两个整型变量并没有交换成功。这就说明了方法调用的本质是:实参把值复制了一份给形参,形参的改变不影响实参。
2.如何理解值传递
通过上面的例子,我们理解了方法调用的本质是:实参把值复制了一份给形参,形参的改变不影响实参。那么这个值究竟是什么值呢?我们得分两种情况讨论:
当然这样说也不是很严谨,因为Java语言本身并不能获得真实对象的内存地址,因为Java并没有指针类型,但是我们可以把某个对象的hashCode近似理解为这个对象的内存地址。
我们看下面实参为引用类型的值传递案例:
public class Demo2 {
public static void change(String str1, StringBuilder str2) {
System.out.println("======in change=======");
System.out.println("str1 = " + str1);
System.out.println("str2 = " + str2);
str1 = "小张";
str2.append(",我们去爬山");
System.out.println("after change....");
System.out.println("str1 = " + str1);
System.out.println("str2 = " + str2);
}
public static void main(String[] args) {
String str = "小明";
StringBuilder str2 = new StringBuilder("今天天气好");
change(str, str2);
System.out.println("======in main after change========");
System.out.println("str = " + str);
System.out.println("str2 = " + str2);
}
}
运行结果:
======in change=======
str1 = 小明
str2 = 今天天气好
after change....
str1 = 小张
str2 = 今天天气好,我们去爬山
======in main after change========
str = 小明
str2 = 今天天气好,我们去爬山
我们发现对于实参是引用类型其本质也是值传递,只不过这个值可以近似理解为引用指向对象的内存地址,而且并没有违反形参的改变不影响实参这一原则。
有初学的小白会问,为什么主方法里的str1没有改变,而str2发生了改变。其实问这个问题,说明他根本没有理解引用类型的本质,简单说,主方法里的str1和str2其实都没有发生改变,str2仅仅是是改变了指向对象的内容,str2这个引用指向的对象地址从来没有发生过改变。该程序内存模型演变过程如下图所示:


因此,我们得出Java值传递的真正含义是:
值传递(pass by value)是指在调用函数时将实参的值复制一份给形参,形参的改变不会影响实参。实参如果是基本数据类型这个值就是变量本身的值,如果是引用数据类型这个值可以近似理解为该引用指向的对象内存地址值。
那么,Java为什么只有值传递呢?这是因为Java本身并没有指针数据类型,我们知道指针是用来保存对象内存地址的一种数据类型,因此在Java中,函数调用时复制的是实参的值,而不是实参变量的地址。 正是因为Java没有指针数据类型,因此Java也没有引用传递,那么什么是引用传递呢?
所谓引用传递是:引用传递(pass by reference)是指在调用函数时将实参变量地址赋值给形参,那么形参的改变就会影响到实参。
咱们看下面C++的字符串引用传递案例,体会形参的改变影响到实参。
#include <iostream>
#include<string>
using namespace std;
void change(string& s, string& sb)
{
s = "小张";
sb +=", 咱们去爬山";
}
int main(int argc, char *argv[]) {
string str="小明";
string sb = "今天天气好";
cout << str << endl;
cout << sb << endl;
change(str,sb);
cout << "======in main after change======" <<endl;
cout << str <<endl;
cout << sb << endl;
return 0;
}
运行结果:
小明
今天天气好
======in main after change======
小张
今天天气好, 咱们去爬山
小结:
Java语言考虑到程序的安全性和健壮性并没有指针类型,因此就无法实现引用传递。Java中的函数调用一律采用值传递,所谓值传递(pass by value)是指:在调用函数时将实参的值复制一份给形参,形参的改变不会影响实参。实参如果是基本数据类型这个值就是变量本身的值,如果是引用数据类型这个值可以近似地理解为该引用所指向对象的内存地址值。