首先,笔者可以说:Java不存在传递引用的情况,Java只有一种传递方法就是传值。
一、首先来明确一下”值传递”和”引用传递的”区别
值传递:是对所传递参数进行一次副本拷贝,对参数的修改只是对副本的修改,函数调用结束,副本丢弃,原来的变量不变(即实参不变)
引用传递:参数被传递到函数时,不复制副本,而是直接将参数自身传入到函数,函数内对参数的任何改变都将反映到原来的变量上。
二、Java中引用与C++引用的区别
C++和Java中都有引用的概念,但在这两种语言中却有完全不同的含义。C++中我们可以用形如”int &b=a”的形式来定义变量a的一个引用b,b只是a的一个别名,b和a在内存中占同一个存储单元,利用引用机制我们可以在调用函数时实现值的双向传递——即引用传递。
看下面代码:
示例一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <iostream> using namespace std; int main() { void swap(int &,int &); int i=3,j=5; swap(i,j); cout<<"i="<<i<<"j="<<j<<endl; return 0; } void swap(int &a,int &b) { int temp; temp=a; a=b; b=temp; } |
执行上面的程序输出的是i=5 j=3,a和b传递给swap()函数的时候,是传递的他们本身的地址,不是他们的拷贝,所以在函数中对他们的改变可以直接影响到实参a和b,这就是引用传递。即:它没有任何值的copy,即使是一个地 址,只是另外一个名字而已。
java中的引用更像C++中的指针,当我们定义一个对象时(比如Person p=new Person()),定义的对象实例会放到java堆中,而变量p(即引用)会放到java栈中,p指向堆中的Person对象实例。
但是java里面没有这样的概念,所有的地址传递其行为是值的传递方式,语义上统一成值传递更为清晰,我们只需要考虑这个值具体是什么,无非两种,要么是基本类型值,要么是个地址。
所以我认为这个“引用”的概念放到java中并不合适。只有值传递的说法更合理。
三、对引用传递的认识误区
为什么有很多人认为java有引用传递呢?一种情况是有人认为调用函数时其参数有可能是引用(如上面的p),所以java有引用传递,这部分人对引用传递根本没有正确的认识;而另一种情况看似有道理,但是仔细分析也是不正确的的,他们往往会用如下的代码来证明他们的观点:
示例二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Test2{ public static void main(String args[]){ Person p1=new Person(15); Person p2=new Person(16); System.out.println(p1.age); System.out.println(p2.age); function(p1,p2); System.out.println(p1.age); System.out.println(p2.age); } private static void function(Person p1,Person p2){ int age; age=p1.age; p1.age=p2.age; p2.age=age; } } class Person{ int age; public Person(int age){ this.age = age; } } |
他们的观点如下:执行上面的代码,调用change()函数以前输出的结果是15、16,调用function()函数之后输出的结果会是16、15,可见在函数内对p1和p2的改变反映到了原来的变量上,要不是不会输出16、15的。
这种解释是很迷惑人的,看上去好像很正确,下面的代码会很好的反驳上面的观点:
示例三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Test3{ public static void main(String args[]){ Person p1=new Person(15); Person p2=new Person(16); System.out.println(p1.age); System.out.println(p2.age); change(p1,p2); System.out.println(p1.age); System.out.println(p2.age); } private static void change(Person p1, Person p2){ Person temp; temp=p1; p1=p2; p2=temp; } } class Person{ int age; public Person(int age){ this.age = age; } } |
执行上面的代码,调用change()前后程序输出的都是15、16,此程序试图通过调用change()交换p1和p2,但是没有成功,为什么呢?因为p1和p2是值传递,change()中的p1和p2是main()函数中p1和p2的副本,调用完change()不会对main()中的变量产生影响。再看示例二中,change()函数内改变的并不是p1和p2本身的值,而是p1和p2指向的对象的值,调用完change()后p1和p2仍然指向函数调用前的堆地址,即函数参数是栈中的p1和p2,而不是堆中p1和p2指向的对象,即使你在函数中改变了堆中的对象,但没有改变函数参数的值。所以示例二并不是什么引用传递;可见java中只有值传递。
除非注明,饮水思源博客文章均为原创,转载请以链接形式标明本文地址