java参数传递时到底是值传递还是引用传递(baidu搜集)
最近比较长一段时间以来,网上的IT同行里面比较流行“JAVA面试32问”,很多人的BLOG里都引用这些面试题,最近因为工作内容比较枯燥,也来看看这些试题以调节一下口味,其中有一道题让我很费解。
原题是:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
用google查询结果,得到答案基本上是:值传递。当时觉得挺纳闷儿,为什么连参数的内容都被修改了,怎么还能说是“值传递”呢?因为在传统的印象里(尤其是从C++过来以后),值传递都应该是不改变原参数的。
问问周围的同事,也大都这么讲,但是也都讲不清这种理论的根源是什么。我这个人有个毛病,有事情想不通时就会憋得难受,后来在《Thinking in Java》的一段内容(注解[1])里找到了自己的结论,我认为(《Thinking in Java》的作者也这么认为):可以说是值传递,也可以说是引用传递。
一,认为是值传递。得出这种结论的前提必须是“参数的值就是对该对象的引用,而不是对象的内容”,这句话可能有些费解,举个例子加以说明。
public class Paier {
public static void main(String[] args) {
Paier paier = new Paier();
paier.test();
}
public void test() {
TestClass para1 = new TestClass();
para1.setTest(new Integer(10));
TestClass result1 = test1(para1);
System.out.println("para1 = " + para1.getTest());
System.out.println("result1 = " + result1.getTest());
TestClass para2 = new TestClass();
para2.setTest(new Integer(10));
TestClass result2 = test2(para2);
System.out.println("para2 = " + para2.getTest());
System.out.println("result2 = " + result2.getTest());
}
public TestClass test1(TestClass t) {
t = new TestClass();
t.setTest(new Integer(20));
return t;
}
public TestClass test2(TestClass t) {
t.setTest(new Integer(20));
return t;
}
class TestClass {
Integer test = null;
public void setTest(Integer i) {
test = i;
}
public Integer getTest() {
return test;
}
}
}
执行后的结果是:
para1 = 10
result1 = 20
para2 = 20
result2 = 20
为什么会这样呢?因为test1想通过修改参数的引用来修改返回值,但是在JAVA中,参数的引用是不可修改的,所以para1和result1分别指向不同的空间,结果也不一样。而在test2中,result2和para2始终指向同一块区域,test2方法修改的是参数内容,而不是参数的引用。
从上面看来,因为参数的引用不可改变,如果理解为“参数的值就是对该对象的引用”,那么java自然只有值传递。
二,认为是引用传递。还是上面的例子,如果在参数传递时理解为“参数的值就是该对象的内容”,那么显然不是值传递,因为对象的内容已经改变了。
认为是引用传递还有一个理由,就是java有一个保留字byvalue,现在的JDK版本中还没有实现这个保留字,似乎是在暗示对这种观点的支持。(There appears to be some support for this view within Sun, since one of the “reserved but not implemented” keywords is byvalue.)
所以说,对于原题的结论,是值传递还是引用传递并不重要,重要的是要理解“对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。”
注解[1]:下面是在《Thinking in Java》中的原文:
This brings up the terminology issue, which always seems good for an argument. The term is “pass by value,” and the meaning depends on how you perceive the operation of the program. The general meaning is that you get a local copy of whatever you’re passing, but the real question is how you think about what you’re passing. When it comes to the meaning of “pass by value,” there are two fairly distinct camps:
1. Java passes everything by value. When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a handle into a method, you get a copy of the handle. Ergo, everything is pass by value. Of course, the assumption is that you’re always thinking (and caring) that handles are being passed, but it seems like the Java design has gone a long way toward allowing you to ignore (most of the time) that you’re working with a handle. That is, it seems to allow you to think of the handle as “the object,” since it implicitly dereferences it whenever you make a method call.
2. Java passes primitives by value (no argument there), but objects are passed by reference. This is the world view that the handle is an alias for the object, so you don’t think about passing handles, but instead say “I’m passing the object.” Since you don’t get a local copy of the object when you pass it into a method, objects are clearly not passed by value. There appears to be some support for this view within Sun, since one of the “reserved but not implemented” keywords is byvalue. (There’s no knowing, however, whether that keyword will ever see the light of day.)
Having given both camps a good airing and after saying “It depends on how you think of a handle,” I will attempt to sidestep the issue for the rest of the book. In the end, it isn’t that important – what is important is that you understand that passing a handle allows the caller’s object to be changed unexpectedly.