原文:https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value
目录
高赞回答一:通过回答一能知道java是值传递还是引用传递
高赞回答二:通过回答二能理解值传递过程中的实质
对这个问题的理解,结合以下两个回答能清楚点,
请看:
高赞回答一:
java总是值传递的,当我们传递一个对象的值时,我们传递的是对象的引用,这就会让我们感到困惑(此处需结合回答二来理解)
比如:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
Dog oldDog = aDog;
// we pass the object to foo
foo(aDog);
// aDog variable is still pointing to the "Max" dog when foo(...) returns
aDog.getName().equals("Max"); // true
aDog.getName().equals("Fifi"); // false
aDog == oldDog; // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// change d inside of foo() to point to a new Dog instance "Fifi"
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}
上例中,aDog.getName()会返回"Max",main方法中的aDog对象的值没有在foo方法中因为Dog("Fifi")改变值,由于对象引用是值传递。如果是引用传递,那么在调用了foo方法后,main方法中的aDog.getName()则会返回“Fifi”。
如下:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
Dog oldDog = aDog;
foo(aDog);
// when foo(...) returns, the name of the dog has been changed to "Fifi"
aDog.getName().equals("Fifi"); // true
// but it is still the same dog:
aDog == oldDog; // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// this changes the name of d to be "Fifi"
d.setName("Fifi");
}
上例中,Fifi是调用foo方法后aDog对象的name,因为对象的name在foo方法中被重新设置了。如何foo方法中对d的操作都如此,都是对aDog的操作,但是都不会改变aDog自身的值。(对于aDog自身的值和对aDog操作后属性的改变,此处可能看着很疑惑,请看回答二)
答主erlando个人主页:https://stackoverflow.com/users/4192/erlando
高赞回答二:
java spec中说到,Java中的一切均是值传递,java中并不存在引用传递。
理解这个的关键在于
Dog myDog;
此处不是一个Dog对象,而是一个指向Dog对象的指针
(指针和引用的区别可参考https://www.cnblogs.com/dolphin0520/archive/2011/04/03/2004869.html)
这是什么意思呢,如下:
Dog myDog = new Dog("Rover");
foo(myDog);
这个过程你本质上传递的将所创建Dog对象的地址给foo方法
(之所以说本质上,是因为java指针不是直接地址,但是把它当成地址是最好理解的)
假设Dog对象的存储地址是42,这就意味着我们传递了42给这个foo方法,如果方法如下定义:
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
我们看看发生了什么:
- 参数someDog设值为42
- 在AAA行,someDog指向的是地址42处所指向的Dog对象;地址42处所指向的Dog对象的name被修改为"Max"
- 在BBB行,一个新的Dog对象被创建,我们假定它的地址是74,我们把参数someDog指定为74了
- 在CCC行,someDog指向的是地址74行所指向的Dog对象;地址74行处所指向的Dog对象的name被修改为"Rowlf"
- 之后,我们返回
现在来看方法外发生了什么:
myDog变了吗?
这就是关键。
记住myDog是一个指针,不是一个实际的Dog对象,因此myDog没变。myDog仍然是值为42,仍然指向原来的Dog对象,(但是注意,由于AAA行,它的name的值现在变为“Max”了,但还是同一个Dog对象,myDog的值没变)
通过地址来改变地址中的内容是完全有效的,但这不会改变地址这个变量。
在C++, Ada, Pascal和其他语言中,支持引用传递,你可以改变所传递的变量。
如果java中有引用传递的语义,那所定义的foo方法在BBB行时,将会改变myDog的指向。
引用变量是所传参数变量的别名,当别名被改变时,所传参数变量也同样被改变。
答主scott-stanchfield个人主页:https://stackoverflow.com/users/12541/scott-stanchfield