对 Java 如何处理聚合和引用感到困惑

Confusion about how Java deals with aggregation and referencing

我对 Java 如何处理对象内的聚合,尤其是对象引用感到困惑。当对象作为参数传递时,它似乎会保留对聚合对象的引用,而不是像我一直相信的那样复制它。

假设我有一个名为 Foo 的基本 class,它包含一个字符串属性、打印函数和文本属性的 setter。

public class Foo {

    private String text;

    public Foo(String text) {
        this.text = text;
    }

    public void print() {
        System.out.println(text);
    }

    public void setText(String text) {
        this.text = text;
    }

}

还有一个名为 Bar 的 class,它包含一个 Foo 类型的属性,以及一个名为 print 的方法,该方法调用 foos print 方法。

public class Bar {

    private Foo foo;

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

    public void print() {
        this.foo.print();
    }

}

如果我定义一个 Foo 的实例,将它传递给一个新的 Bar 实例,然后调用 bars 打印方法,它将按我的预期打印 "hello"。但是,如果我随后使用其 setter 方法将 foo 的原始实例的文本设置为 "Edited",bars print 方法也将打印 "Edited".

public static void main(String[] args){

    Foo foo = new Foo("Hello");
    Bar bar = new Bar(foo);
    bar.print();
    foo.setText("Edited");
    bar.print();

}

Console Output

Bar 对象似乎保留了对 Foo 对象的引用,即使我将它作为参数传递也是如此。我确定我在这里遗漏了一些微不足道的东西,我只是希望有人能清楚地解释一下。

"I'm sure I am missing something trivial here"

不是真的。您看到的不是错误,而是功能。在 java 中传递对象意味着传递对它们的 引用 。除非代码通过 .clone() 明确请求,否则对象不是 "cloned"。在此站点上搜索 "is java pass-by-value or pass-by-reference" 应该可以帮助您找到所需的所有详细说明。

引用已复制,不是对象

It seems like objects will keep a reference to an aggregated object when it is passed as a parameter, rather than copying it like I've been lead to believe.

否,对象复制

引用(指针)在作为参数传递给方法时被复制。对象(指称,指针指向的东西)是复制的。

指针实际上是内存中某处的地址(尽管我们 Java 程序员并不这么认为)。该地址,基本上是一个数字,在传递给另一个方法时被复制。但是该对象未被触及,不知道任何引用。

这是内存中几个 Cat 对象的图表。五个中的三个是 garbage-collection 的候选者,因为它们没有剩余的引用,也没有指向它们的指针。一开始 Lilly Mae cat 有一个指针指向它。

Cat c = new Cat( "Lilly Mae" ) ;

c变量不是持有Cat,它持有内存中其他地方的地址,您可以在其中找到Cat对象.将该行视为:

variable-holding-pointer-that-can-only-point-to-objects-of-class-Cat c = instantiate-new-object-of-type-Cat-and-return-a-pointer-to-its-location-in-memory( "Lilly Mae" ) ;

然后我们将该指针作为参数传递给另一个方法。

Human h = SubservientHumanLocator.findFirstAvailable() ;
h.feedCat( c ) ;  // Passing a copy of the address of Lilly Mae to this other object’s method.

Cat 的引用传递给 Human 后,我们仍然只有一只饿猫。我们没有克隆猫。我们现在有 两个 引用,都指向同一个原始 Cat 对象。

这是 Human::feedCat 调用前后的状态图。

在调用人类喂猫后,变量 c 可能会超出范围。在喂猫之后,Human 对象 h 可能会让它的复制引用也超出范围。然后,如果没有其他引用,并且没有现有的引用,我们的 Cat 对象将成为垃圾收集的候选对象。