问题
我一直认为Java是通过引用传递。
不过,我见过一些博客文章(例如this blog),声称它不是。
我不理解它们的区别,能解释一下吗?
#1 热门回答(4651 赞)
Java永远是传递值.不幸的是,他们决定将对象的位置称为“引用”。当我们传递一个对象的值时,我们将它传递给它。这对初学者来说很混乱。
它是这样的:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
// 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, java passes by value
aDog.getName().equals("Fifi"); // false
}
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”改变。如果通过引用传递,那么
main中的
aDog.getName()在调用
foo后会返回
'Fifi'`。
同样:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
foo(aDog);
// when foo(...) returns, the name of the dog has been changed to "Fifi"
aDog.getName().equals("Fifi"); // 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)
后的狗的名字,因为该对象的名字被设置在foo(...)
的内部。任何foo
在d
上执行的操作都是这样的,对于所有实际的目的,它们都是在aDog
本身上执行的(除非d
被改变为指向不同的Dog
实例,比如d = new狗(“拳击手”)
)。
#2 热门回答(2601 赞)
我只是注意到你引用了my article。
Java Spec说,Java中的所有东西都是按值传递的。 Java中没有“传递参考”这样的东西。
理解这一点的关键是类似的东西
Dog myDog;
isnota狗;它实际上是一只狗。
这意味着什么,当你有
Dog myDog = new Dog("Rover");
foo(myDog);
你基本上是把创建的Dog
对象的地址传递给foo
方法。
(我说的本质上是因为Java指针不是直接地址,但最简单的方法就是这样想)
假设Dog
对象驻留在内存地址42上。这意味着我们将42传递给该方法。
如果方法被定义为
public void foo(Dog someDog) {
someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC
}
让我们看看发生了什么。
- 参数someDog被设置为值42
- 在“AAA”行,有人跟踪到它指向的Dog(地址42处的Dog对象),Dog(地址为42的那个)被要求将他的名字改为Max
- 在“BBB”行创建一个新的狗。假设他在地址74,我们将参数someDog分配给74
- 在“CCC”线上,有人跟踪它指向的Dog(地址为74的Dog对象),Dog(地址为74的那个)被要求将他的名字改为Rowlf
- 然后,我们回来
现在让我们考虑一下在该方法之外发生的事情:myDog
改变了吗?
有关键。
记住myDog
是apointer,而不是实际的Dog
,答案是NO。 myDog
仍然有值42;它仍然指向原来的狗
(但请注意,因为行“AAA”,它的名字现在是“最大” - 仍然是狗;myDog
的值没有改变。)
这是完全有效的tofollowan地址,并改变它的结尾;但不会改变变量。
Java的工作方式与C完全一样。您可以指定一个指针,将指针传递给方法,跟随方法中的指针并更改指向的数据。但是,您无法更改指针指向的位置。
在C,Ada,Pascal和其他支持通过引用的语言中,实际上可以更改传递的变量。
如果Java具有传引用语义,那么我们上面定义的foo
方法会在'myDog指向它在BBB行分配'someDog
时指向的地方。
将引用参数看作是传入变量的别名。当分配了别名时,传入的变量也是如此。
#3 热门回答(1374 赞)
Java总是通过值传递参数而不是通过引用。
让我通过example解释这一点:
public class Main{
public static void main(String[] args){
Foo f = new Foo("f");
changeReference(f); // It won't change the reference!
modifyReference(f); // It will modify the object that the reference variable "f" refers to!
}
public static void changeReference(Foo a){
Foo b = new Foo("b");
a = b;
}
public static void modifyReference(Foo c){
c.setAttribute("c");
}
}
我将逐步解释这一点:
- 声明一个名为f的Foo类型的引用,并将其赋值给一个具有属性“f”的Foo类型的新对象。 Foo f = new Foo(“f”);
- 从方法的角度来说,名为a的Foo类型的引用被声明,并且它最初被赋值为null。 public static void changeReference(Foo a)
- 在调用方法changeReference时,引用a将被分配给作为参数传递的对象。 changeReference(F);
- 声明一个名为b的Foo类型的引用,并将其赋值给一个具有属性“b”的Foo类型的新对象。 Foo b =新Foo(“b”);
- a = b将引用a NOT f重新分配给其属性为“b”的对象。
- 在调用modifyReference(Foo c)方法时,会创建一个引用c并将其分配给具有属性“f”的对象。
- c.setAttribute(“c”);将改变引用c点的对象的属性,并且引用f指向它的是同一个对象。
我希望你现在能理解如何将对象作为参数传递给Java。