【问题标题】:Is Java really passing objects by value? [duplicate]Java 真的是按值传递对象吗? [复制]
【发布时间】:2011-12-15 03:53:37
【问题描述】:

可能重复: Is Java pass by reference?

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并改变它呢?

【问题讨论】:

  • 这已经被问了一百万次了。

标签: java parameter-passing terminology pass-by-reference pass-by-value


【解决方案1】:

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- 声明一个名为 f 类型为 Foo 的引用,并将其分配给一个类型为 Foo 的新对象,其属性为 "f"

Foo f = new Foo("f");

2- 在方法方面,声明了一个名为 a 的类型为 Foo 的引用,并且它最初分配给 null

public static void changeReference(Foo a)

3- 当您调用方法changeReference 时,引用a 将被分配给作为参数传递的对象。

changeReference(f);

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

Foo b = new Foo("b");

5- a = b 正在将引用 a NOT f 重新分配给其属性为 "b" 的对象。


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

7-c.setAttribute("c");会改变引用c指向它的对象的属性,引用f指向它的是同一个对象。

我希望您现在了解在 Java 中如何将对象作为参数传递:)

【讨论】:

  • 非常感谢您详细解释。
  • 现在我明白了“引用 obj 作为值”或“发送引用副本”的含义:)
  • 这些图表确实有助于让这些概念变得清晰!谢谢!
  • 这个解释很清楚,但我还是有点糊涂。在步骤3中,“a”指向一个属性为f的对象。对我来说,“a”似乎指向了“f”也指向的对象的引用。如果对象是按值传递的,“a”和“f”都应该有自己的对象。但是,它们实际上共享相同的对象(即它们指向相同对象的引用)。
  • @Hiroki 对象不是按值传递的。它是指向按值传递的对象的指针。 Java 中的变量不能包含对象,它们总是包含指向对象的指针。因此,对象不能传递给方法,它始终是指向对象的指针,通过值传递给方法。
【解决方案2】:

您正在更改obj属性,而不是更改obj(参数)本身。

关键是,如果您将 obj 指向 changeName 中的其他内容,那么 更改将不会反映在 main 中。

请参阅this post 以获得进一步说明。

【讨论】:

    【解决方案3】:

    它将对 obj 的引用作为值传递(我知道有点混乱 :))。

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

    这意味着您可以执行以下操作:

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

    和声明

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

    仍将引用旧对象(您设置名称的那个)。

    【讨论】:

      【解决方案4】:

      在 Java 中,对象句柄或对象的标识被视为一个值。按值传递意味着传递此句柄,而不是对象的完整副本。

      “按引用传递”一词中的“引用”也不意味着“对对象的引用”。它的意思是“对变量的引用”——函数定义(或者更确切地说,调用框架)中的一个命名“桶”,可以存储一个值。

      通过引用传递意味着被调用的方法可以改变调用方法中的变量值。 (例如,在 C 标准库中,函数 scanf 以这种方式工作。)这在 Java 中是不可能的。您可以随时更改对象的属性——它们不被视为其“价值”的一部分。它们是完全不同的独立对象。

      【讨论】:

      • 我很欣赏这个答案,因为它解释了在 Java 中对象属性不被视为其“价值”的一部分。但在我的世界里,允许具有自己作用域的函数或方法修改超出其作用域且在调用方法作用域内的变量(或对象)是“按引用传递”,抱歉 Java 人。
      • 那你的意思是什么?不同的语言有不同的概念模型和不同的术语。只要“你的世界”不是 Java 世界,也不是 Java 亲戚的世界,它在这里并不真正相关,不是吗?我也可以争辩说 PHP 和 Perl 是通过本机实现“pass-by-deep-copy”的奇怪的,但这只是语义,对任何人都没有用。 Java 的术语与 C 的工作方式大致一致——传递 foo&foo 的区别。
      • 在 C++(Java 的另一个祖先)中,通过引用而不是值再次传递与函数是否直接在堆栈帧中更改状态无关。这就是const 的用途。 (尽管 C++ 具有非凡的灵活性,当然也可以按您的意愿通过值传递来复制对象。)在这些语言中,引用或多或少意味着您可以为其赋值的(局部)变量 并在当前范围之外更改状态。不仅仅是指向可能非本地状态的任何变量。
      • 这实际上主要是关于你思考事物的水平,以及你认为的变量的“价值”。在底层,Java 变量是一小块内存的地址名称,其中包含例如 8 个字节的数据。 (Java 不会对数据结构进行堆栈分配,我认为旧版本的 C 也不会这样做,PHP 可能也不会。)如果它是简单数据类型或整数,则该内存直接包含数据,或者它包含更大内存块的另一个内存地址。当我们谈论变量的值时,我们指的是那 8 个字节。
      • 感谢您的额外澄清,可能会减去态度。其他部分很有帮助。
      【解决方案5】:

      它没有改变 obj (你的代码也没有改变它)。 如果它是通过引用传递的,你可以这样写:

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

      并通过 main 方法打印“anotherName”。

      【讨论】:

      • 只是一个小注释:myObject 应该是 MyObject。
      【解决方案6】:

      Java 正在将您所传递的内容的副本传递给您的函数。当它是原始类型时 - 它将是一个值的副本。当它是一个对象时 - 您正在传递参考副本。在您的代码示例中,您正在修改对象属性之一,而不是引用本身,因此名称将被更改。但是,当您想在 changeName 函数中将新对象分配给 obj 变量时,您正在更改引用,因此外部 obj 将具有旧值。

      【讨论】:

        猜你喜欢
        • 2011-11-21
        • 2017-02-22
        • 1970-01-01
        • 2013-01-21
        • 1970-01-01
        • 2020-05-28
        • 2012-04-25
        • 2020-03-17
        • 2012-07-21
        相关资源
        最近更新 更多