【问题标题】:Is passing object to constructor pass by reference [duplicate]将对象传递给构造函数通过引用传递[重复]
【发布时间】:2013-10-21 02:37:18
【问题描述】:

假设我有一个名为 TestClass 的类和构造函数。

public class TestClass {
    Foo foo;

    public TestClass(Foo foo) {
        this.foo = foo; 
    }
}

这里,构造函数接受一个对象,它是类Foo 的一个实例。假设我的static void main(String[] args) 执行以下操作,与任何TestClass 完全分开;

  • (1) 实例化foo

  • (2) 将实例foo 传递给TestClass 构造函数

  • (3)改变foo的内部状态

在第(3)步之后,foo在我的TestClass实例也的状态会改变吗?

【问题讨论】:

  • 您也可以编写一个小程序来亲自查看,但我承认为什么会发生这种情况并不一定是您可以通过尝试小示例来了解的。
  • @DennisMeng 这是真的,但我自己这样做不会揭示任何有经验的回答者可以涵盖的边缘情况。
  • 回答你的题目,Java 没有通过引用。但是,它通过值传递引用,这导致了您看到的问题。

标签: java constructor scope


【解决方案1】:

它不是通过引用传递的。相反,它是通过引用的值传递的,这是一个微妙但重要的区别。

在您在 main() 方法的其余部分中改变 foo 之后,foo 字段也将显示这些突变,正如您所说,因为两个变量都指向同一个实例。但是,如果您重新分配 foo 到新的东西,foo 字段将不会更改。如果foo 真的是通过引用传递的,这将不是真的。简而言之,Java 中的一切都是按值传递的;碰巧对象是通过引用来处理的,所以这些引用的值被传递了。

我将尝试用一个例子来说明这一点。考虑以下类:

class A {
    public int n;

    public A(int n) {
        this.n = n;
    }
}

还有以下方法:

public static void mutate(A a) {
    a.n = 42;
}

现在我们可以有这样的东西:

A a = new A(0);
A.mutate(a);

System.out.println(a.n);
42

我们可以看到a 的状态在mutate() 中发生了变化。现在让我们修改静态方法:

public static void mutate(A a) {
    a = new A(42);
}

再试一次:

A a = new A(0);
A.mutate(a);

System.out.println(a.n);
0

如您所见,a 的状态没有改变。如果引用已被传递给函数,我们预计重新分配的效果会明显超出方法的范围。然而,some 引用实际上被传递了,因为改变参数也会导致方法之外的变化。

【讨论】:

  • +1 仅用于第一段
【解决方案2】:

是的,因为您将相同的对象分配给 Foo 类的另一个引用,即对象相同,但被两个引用引用。

【讨论】:

  • 随着讨论的进行,原始类型的场景肯定是不同的。 :)
【解决方案3】:

在步骤(3)之后,我的 TestClass 实例中的 foo 是否也有 它的状态改变了吗?

是的。

您可能想阅读this

更新...

现在,假设您将原始值传递给构造函数...

public class TestClass {
    int foo;

    public TestClass(int foo) {
        this.foo = foo; 
    }

    public String toString() {
        return "TestClass: " + foo;
    }
}

public static void main(String args[]) {
    int myFoo = 1;
    TestClass test = new TestClass(myFoo);
    myFoo += 2;
    System.out.println("myFoo = " + myFoo);
    System.out.println("yourFoo = " + test);
}

这将输出...

myFoo = 3
yourFoo = 1

这证明了改变原语的值不会改变构造函数/方法维护的值。

同样,如果你在传递对象引用后更改它

public class TestClass {
    Foo foo;

    public TestClass(Foo foo) {
        this.foo = foo; 
    }

    public Foo getFoo() {
        return foo;
    }
}

public static void main(String args[]) {
    Foo myFoo = new Foo();
    TestClass test = new TestClass(myFoo);
    myFoo = new Foo();
    System.out.println("myFoo == yourFoo = " + myFoo.equals(test.getFoo()));
}

会输出

myFoo == yourFoo = false

由于对象引用不一样。

【讨论】:

  • 值得注意的是,如果你有一个原始类型,而不是一个对象类型(例如,int 而不是Foo),那么答案是no.
  • @DavidWallace 当然不会对此提出异议,但这与说“如果您在将Foo 传递给构造函数后创建了一个新实例,则不会更改内部状态Foo 在TestClass 内,但这不是问题……反正我读到了……这一切都让我头晕目眩:P
  • 当然。不在那里争论。我只是在考虑 OP 关于边缘情况的评论,并想知道“原始”是否会包含在该类别中。如果没有该评论,这确实是一个是/否的问题。
  • @DavidWallace 这实际上是一个公平的评论
猜你喜欢
  • 2013-11-13
  • 1970-01-01
  • 2012-03-19
  • 2020-07-27
  • 1970-01-01
  • 2014-06-04
  • 2016-02-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多