Java究竟是值传递还是引用传递,也许很多人会不假思索的回答说它是引用传递,也有人说基本类型是值传递,引用类型对应引用传递,其实这些说法既不是完全正确,也不是完全错误,不同语言有着不同的语法或者概念,但如果就计算机传值的本质来说,都应该是值传递,Java本质上说也是值传递的。
假如按照引用传递的思想,我们想要通过一个函数来修改String对象,首先想到可以这样写:
String a = "A";
// 试图将A该成B
change(a);
System.out.println(a); // 结果:A
public void change(String a) {
a = "B";
//a = new String("B");
}
结果并没有改变String的值,即使使用new的方式也得到一样的结果,但是用C/C++,是不一样的结果,A会被改成B,为什么会有如此大的区别呢?以下用图来说明。
首先创建一个String对象,具体过程是,首先在堆中分配合适的空间给字符串“A”的实例,然后在栈顶的frame分配一定的空间给变量a,然后给变量a分配字符串实例“A”在堆中的地址,也就是一条引用reference,可以认为这条引用是变量a的属性。
重点来了,按值传递,意味着变量a进入到change()方法入口时,栈顶的frame会创建一个a的拷贝a',a'具有和a一样的reference,共同指向“A”,试想,如果是按引用传递,就没必要再创建一个参数的拷贝了吧,不管如何改变都是对原始引用变量的改变。
接着,我们要改变a的值,又在堆中创建了字符串实例“B”,这时候对变量a的赋值操作,就是转而对变量a'进行操作,a'的reference理所当然改为指向“B”,而原始变量a的reference没有任何影响。
以上,可以一句话概括为,当一个引用变量a传递给方法的参数时,实际传递给方法的,是变量a的副本a',方法围绕着a'进行操作,这就是按值传递。所以,以下代码的输出结果是?
String a = "A";
String b = "B";
swap(a, b);
System.out.println(a);
System.out.println(b);
那么问题来了,如果我们想做到传“引用”,或者传“地址”,如何实现呢?其实,只要不改变拷贝值的reference,不对他重新赋值不就行了吗?没有赋值,没有new操作,自然不会产生新的reference,例如可以使用StringBuilder类来作为参数,StringBuilder类是可变的,并且它的操作并不会修改reference。
StringBuilder sb = new StringBuilder("A");
change(sb);
System.out.println(sb);
public void change(StringBuilder sb) {
// 引用仍然指向原始的堆空间,只是原始堆空间的值被改变
sb.delete(0, 1);
sb.append("B");
}