Java是否真的通过值传递对象?

45

可能的重复问题: Java是按引用传递吗?

public class myClass{
    public static void main(String[] args){
        myObject obj = new myObject("myName");
        changeName(obj);
        System.out.print(obj.getName()); // This prints "anotherName"
    }
    public static void changeName(myObject obj){
        obj.setName("anotherName");
    }
}

我知道Java是按值传递,但为什么在前面的例子中它通过引用传递obj并改变它呢?


12
这个问题已经被问了无数次。 - G_H
你的例子说明了在Java被称为“按引用调用”或“对象按引用传递”之前的逻辑。现在当涉及到Java时,有一个岛上的教众信奉“Java是按值传递”的教义,并根据这个教义扭曲整个世界以使其成立,但“Java是按值传递”本身就是语法上的荒谬。根据这个教义,例如一个对象不是一个值,而是一个对象的引用。 - Sam Ginrich
6个回答

129

Java总是按值传递参数,而不是按引用传递。在您的示例中,您仍然通过其值传递obj,而不是引用本身。在您的方法changeName中,您将另一个(本地)引用obj分配给作为参数传递的同一对象。一旦您修改该引用,就会修改作为参数传递的原始引用obj


编辑:

让我通过一个例子来解释这个问题:

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 change the object that the reference refers to!
     }
     public static void changeReference(Foo a)
     {
          Foo b = new Foo("b");
          a = b;
     }
     public static void modifyReference(Foo c)
     {
          c.setAttribute("c");
     }
}

我将逐步解释这个问题:
1- 声明一个类型为 Foo 的引用变量 f,并将其赋值为一个带有属性 "f" 的新的 Foo 对象。
Foo f = new Foo("f");

在这里输入图片描述

2- 从方法的角度来看,声明了一个类型为Foo、名为a的引用,并将其初始值赋为null

public static void changeReference(Foo a)

在此输入图片描述

3- 当您调用方法changeReference时,引用变量a将被赋值为传递给该方法的对象。

changeReference(f);

图片描述

4- 声明一个名为b的引用,类型为Foo,并将其分配给具有属性"b"Foo类型的新对象。

Foo b = new Foo("b");

Enter image description here

5- 当你使用 a = b 时,实际上是将引用 a 而不是 f 重新指向其属性为 "b" 的对象。

Enter image description here


6- 当你调用 modifyReference(Foo c) 方法时,一个名为 c 的引用被创建并指向其属性为 "f" 的对象。

Enter image description here

7- c.setAttribute("c"); 将会修改引用 c 所指向的对象的属性,而这个对象也是引用 f 指向的对象。

Enter image description here

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


6
非常感谢您详细地解释。 - MBZ
3
现在我明白了“将对象作为值传递”或“复制引用”的含义 :) - Jemshit Iskenderov
1
这些图表真的帮助澄清了这些概念!谢谢! - VIN
3
这个解释非常清楚,但我仍然有点困惑。在第3步中,“a”指向一个属性为f的对象。对我来说,“a”似乎指向“f”所指向的对象的引用。如果对象是按值传递的,那么“a”和“f”都应该有自己的对象。然而,它们实际上共享同一个对象(即它们都指向相同对象的引用)。 - Hiroki
3
对象并不是按值传递的,而是对象指针按值传递。在Java中,变量不能包含对象,它们总是包含指向对象的指针。因此,对象不能被传递到方法中,总是将指向对象的指针按值传递给方法。 - fishinear
显示剩余2条评论

9
在Java中,对象句柄(或称对象标识)被视为一个值。按值传递意味着传递该句柄,而不是对象的完整副本。
"引用"在术语“按引用传递”中也并不意味着“对象的引用”。它指的是对变量的引用 - 函数定义(或者更准确地说是调用帧)中可以存储值的命名“桶”。
按引用传递意味着被调用方法可以更改调用方法中的变量值。(例如,在C标准库中,函数scanf就是这样工作的。)这在Java中是不可能的。您始终可以更改对象的属性 - 它们不被认为是其“值”的一部分。它们是完全不同的独立对象。

2
我很欣赏这个答案,因为它解释了在Java中对象属性不被视为其“值”的一部分。但在我的世界里,允许具有自己作用域的函数或方法修改在其范围之外且在调用方法的范围内的变量(或对象)就是“按引用传递”,对不起,Java的朋友们。 - Tom Auger
那么你的观点是什么?不同的编程语言有不同的概念模型和术语。只要“你的世界”不是Java世界或者Java的衍生物之一,这就不是真正相关的,对吧?我同样可以主张PHP和Perl是通过本地实现“深度复制传递”的奇怪语言,但这只是语义学上的问题,对任何人都没有用处。Java的术语与C语言的工作方式大致一致——传递foo&foo之间的区别。 - millimoose
在C++中,另一个Java的祖先,通过引用而不是值传递与函数是否直接在堆栈帧中更改状态无关。这就是“const”的作用。 (尽管考虑到C++的非凡灵活性,通过值传递可以按任何您希望的方式复制对象。)在这些语言中,引用或多或少意味着您可以为其分配一个值并在当前范围之外更改状态的(本地)变量。不仅仅是指向可能非本地状态的任何变量。 - millimoose
这主要取决于你思考问题的层次以及你认为变量的“价值”是什么。在低层次上,Java变量是一个小块内存的地址名称,其中包含8个字节的数据。(Java不会对数据结构进行堆栈分配,我认为旧版本的C也不会,PHP可能也不会。)如果它是一个简单的数据类型或整数,则该内存直接包含数据;否则,它包含另一个更大块内存的内存地址。当我们谈论变量的值时,我们指的是那8个字节。 - millimoose
谢谢您提供的额外澄清,也许可以不带情绪。其他部分很有帮助。 - Tom Auger
除非“变量”确切地是一个对象,否则不存在“对变量的引用”。 - Sam Ginrich

3

它没有改变obj(无论如何,您的代码都不会更改它)。 如果它是通过引用传递的,您可以这样写:

public static void changeName(myObject obj){
    obj = new myObject("anotherName");
}

在主方法中打印出“anotherName”。


只是一个小提醒:myObject 应该改为 MyObject。 - Laszlo Varga

2
你正在改变obj的一个属性,而不是改变obj(参数)本身。
重点是,如果你在changeName中将obj指向其他东西,那么这个变化不会反映在main中。
请参见此帖子以获取更多澄清。

1

它将 obj 的引用作为值传递(有点令人困惑,我知道 :))。

所以假设它复制了指向 obj 值的指针并传递了它。

这意味着你可以做一些像这样的事情:

  public static void changeName(myObject obj){    
        obj.setName("anotherName");
        obj = new myObject();
    }

以及该语句

System.out.print(obj.getName());

仍然会引用旧对象(你进行setName操作的那个对象)。


至今还没有看到“引用”与“值传递”的定义对比,因此尚不清楚对象是通过何种方式传递的,因为实际使用情况是将对象传递给某个方法,该对象在调用方和被调用方法之间共享,其中引用作为技术方面出现,实际上并没有触及共享对象的语义。 - Sam Ginrich

0

Java在传递参数时是传递其副本。当参数是基本类型时,它将是值的副本。当参数是对象时,你传递的是引用的副本。在你的代码示例中,你修改了一个对象的属性,但没有修改引用本身,因此名称将被更改。然而,当你想要在changeName函数中将新对象分配给obj变量时,你正在改变引用,因此外部的obj将具有旧值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接