我一直以为 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 中:)