Java是“通过引用传递”还是“按值传递”?
我一直认为Java是通过引用 ; 然而,我看到了一些博客文章(例如,这个博客),声称它不是。我不认为我理解他们的区别。
什么是解释?
- 我相信在这个问题上的混淆很大程度上与不同的人对“参考”这个词有不同的定义。来自C ++背景的人认为“参考”一定意味着在C ++中意味着什么,来自C背景的人们假定“参考”必须与其语言中的“指针”相同,等等。说Java通过引用传递是否正确取决于“引用”是什么意思。
- 我试图始终如一地使用评估策略文章中的术语。应该指出的是,尽管文章指出社区的条款差异很大,但它强调语义
call-by-value
和call-by-reference
差异是非常关键的。(我个人更喜欢使用call-by-object-sharing
这些日子call-by-value[-of-the-reference]
,因为这描述了高层次的语义,并没有产生冲突call-by-value
,这是潜在的实现。) - 你能把你的评论放在一个巨大的广告牌上吗?这就是整个问题。这表明这整个事情是语义。如果我们不同意参考的基本定义,那么我们不会同意这个问题的答案:)
- 我宁愿问是否在调用时复制传递给方法的值。我想这是你正在寻找的问题。
- 我认为混淆是“通过引用”与“引用语义”。Java是通过引用语义传递值的。
答案一:
Java始终是按值传递的。不幸的是,他们决定把对象的位置称为“参考”。当我们传递一个对象的值时,我们传递给它的引用。这对初学者来说很混乱。
它是这样的:
public static void main( String[ ] args ) { Dog aDog = new Dog("Max");<span data-mce-type="bookmark" style="display: inline-block; width: 0px; overflow: hidden; line-height: 0;" class="mce_SELRES_start"></span> // 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"
。值aDog
内main
未在功能改变foo
与Dog
"Fifi"
作为对象基准由值来传递。如果通过引用传递,那么aDog.getName()
在调用之后,in main
将返回。"Fifi"
foo
同样:
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 Spec说,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
被设置为值42 - 在“AAA”行
someDog
跟着Dog
它指向(Dog
地址为42 的对象)Dog
(地址42的那个)被要求将他的名字改为Max
- 在“BBB”行
- 一个新
Dog
的创建。假设他在地址74 - 我们将参数分配
someDog
给74
- 一个新
- 在“CCC”行
- someDog跟着
Dog
它指向(Dog
地址为74 的对象) Dog
(地址为74的那个)被要求将他的名字改为Rowlf
- someDog跟着
- 那么,我们回来
现在让我们考虑一下在这个方法之外发生的事情:
有myDog
改变吗?
有关键。
记住这myDog
是一个指针,而不是实际的Dog
,答案是否定的。myDog
仍然有价值42; 它仍然指向原来的Dog
(但注意,由于“AAA”行,它的名字现在是“最大” – 仍然相同的狗myDog
的价值没有改变。)
遵循地址并改变结尾是完全有效的; 那不改变变量,但是。
Java的工作方式与C完全一样。您可以指定一个指针,将指针传递给某个方法,按照方法中的指针并更改指向的数据。但是,您不能更改指针指向的位置。
在C ++,Ada,Pascal和其他支持通过引用的语言中,实际上可以更改传递的变量。
如果Java具有引用传递语义,那么foo
我们在上面定义的方法将在BBB上myDog
分配的位置发生改变someDog
。
将引用参数看作是传入变量的别名。当分配了别名时,传入的变量也是如此。