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”
。 在函数foo
中,main
中的值adog
不会因对象引用通过值传递而改变,函数中的dog
“fifi”
带有dog
。 如果它是通过引用传递的,那么main
中的adog.getName()
将在调用foo
之后返回“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)
之后的狗的名称,因为对象的名称是在foo(...)
中设置的。 foo
对d
执行的任何操作都是在ADOG
上执行的,但不可能更改变量ADOG
本身的值。
我刚注意到你引用了我的文章。
Java的规范说,Java的一切都是通过价值传递的。 Java不存在“过来人”的说法。
理解这一点的关键是
Dog myDog;
不是狗; 其实是指向狗的指针。
这意味着,当你
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
设置为值42someDoG
后面跟着doG
它指向的doG
对象(地址42处的doG
对象)狗
(地址42的那只)被要求把它的名字改成Max狗
。 假设他在74someDog
分配给74dog
它指向的dog
对象(地址74处的dog
对象)狗
(地址74处的那个)被要求把他的名字改成rowlf现在让我们思考一下方法之外会发生什么:
MyDog
是否更改?
钥匙在那儿。
请记住,mydog
是一个指针,而不是一个实际的dog
,答案是否定的。 mydog
仍然具有值42; 它仍然指向最初的狗
(但是注意,由于行“aaa”,它的名称现在是“max”--仍然是同一个狗;mydog
的值没有改变。)
跟随地址并更改地址末尾的内容是完全有效的; 但是,这不会改变变量。
Java的工作原理与C完全相同。您可以分配一个指针,将指针传递给一个方法,跟随方法中的指针并更改所指向的数据。 但是,您不能更改该指针指向的位置。
在C++,Ada,Pascal和其他支持按引用传递的语言中,您实际上可以更改传递的变量。
如果Java具有按引用传递语义,那么我们上面定义的foo
方法在BBB行分配someDog
时,将会改变myDog
所指向的位置。
将引用参数视为传入变量的别名。 当该别名被赋值时,传入的变量也被赋值。
Java总是按值传递参数,而不是按引用传递参数。
让我通过一个例子来说明这一点:
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");
在方法端,声明了一个类型为foo
,名称为a
的引用,并且最初为它分配了null
。
public static void changeReference(Foo a)
当您调用方法ChangeReference
时,引用a
将被分配作为参数传递的对象。
changeReference(f);
声明一个类型为foo
的名为b
的引用,并为它分配一个类型为foo
的新对象,该对象带有一个属性“b”
。
Foo b = new Foo("b");
a=b
对其属性为“b”
的对象的引用a
而不是f
进行新赋值。
当您调用ModifyReference(Foo c)
方法时,将创建一个引用c
,并将其分配给具有属性“f”
的对象。
C.SetAttribute(“C”);
将更改引用C
指向它的对象的属性,以及引用F
指向它的对象的属性。
我希望您现在明白了在Java中传递对象作为参数是如何工作的:)