我一直以为 Java 传参时是传引用的 。
但是我看过的一些博客文章(例如这篇)却说 Java 是 “传值” 的。
我不太理解这些文章中所说的 “传值” 和 “传引用” 的区别。
有谁能再给我解释一下吗?
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
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" 。使用Dog "Fifi"在函数foo不更改main的值aDog ,因为对象引用按值传递。如果它是通过引用传递的,那么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 Dog("Boxer") )。
我刚刚注意到你引用了我的文章 。
Java 规范说 Java 中的所有内容都是按值传递的。在 Java 中没有 “pass-by-reference” 这样的东西。
理解这一点的关键是类似的东西
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设置为值 42 someDog之后到Dog它指向(在Dog对象在地址 42) Dog (地址为 42 的那个)将他的名字改为 Max Dog 。让我们说他在地址 74 someDog分配给 74 Dog它指向(在Dog对象在地址 74) Dog (地址为 74 的那个)将他的名字改为 Rowlf 现在让我们考虑一下方法之外会发生什么:
myDog改变了吗?
关键是。
记住myDog是一个指针 ,而不是一个真正的Dog ,答案是否定的。 myDog的值仍为 42; 它仍然指向原始的Dog (但请注意,由于行 “AAA”,它的名称现在是 “Max” - 仍然是相同的狗; myDog的值没有改变。)
按照地址并改变最后的内容是完全有效的; 但是,这不会改变变量。
Java 的工作方式与 C 完全相同。您可以分配指针,将指针传递给方法,按照方法中的指针操作并更改指向的数据。但是,您无法更改指针指向的位置。
在 C ++,Ada,Pascal 和其他支持 pass-by-reference 的语言中,您实际上可以更改传递的变量。
要是 Java 传递 by-reference 语义中, foo我们在上面定义的方法会改变其中myDog指着时分配someDog上线 BBB。
将引用参数视为传入的变量的别名。分配该别名时,传入的变量也是如此。
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并将其分配给类型的新对象Foo与属性"f" 。
Foo f = new Foo("f");
从方法方面,声明了具有名称a的类型为Foo的引用,并且它最初被赋值为null 。
public static void changeReference(Foo a)
当您调用方法changeReference ,引用a将被分配给作为参数传递的对象。
changeReference(f);
声明名为参考b类型的Foo并将其分配给类型的新对象Foo与属性"b" 。
Foo b = new Foo("b");
a = b将引用a NOT f重新分配给其属性为"b"的对象。

当您调用modifyReference(Foo c)方法时,将创建引用c并将其分配给具有属性"f"的对象。

c.setAttribute("c");将更改引用c指向它的对象的属性,并且它是引用f指向它的同一对象。

我希望你现在明白如何将对象作为参数传递在 Java 中:)