引用和复制对象,意外行为

时间:2013-04-07 20:07:25

标签: java

我需要知道为什么会发生以下情况,在这段代码中(最后两个块)我希望完全相同的输出但是本地对象(仅仅引用列表中的那些,对吧?)是旧的列表更新时的状态。我有一个错误,因为我的游戏代码中有类似的复制过程(基于tile,对象交换位置,所以我想为什么不只是交换他们的引用...)

示例:

package game;

import java.util.ArrayList;


public class Tester {
private String s;
private Foo foo;

public Tester(String s, String f) {
    this.s = s;
    this.foo = new Foo(f);
}

class Foo {
    private String f;

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

@Override
public String toString() {
    return foo.f + ":" + s;
}
public void swap(Tester other) {
    String tempS = this.s;
    Foo tempFoo = this.foo;

    this.s = other.s;
    this.foo = other.foo;
    other.s = tempS;
    other.foo = tempFoo;

}



public static void main(String[] args) {
    ArrayList<Tester> test = new ArrayList<Tester>();
    test.add(new Tester("First", "1"));
    test.add(new Tester("Second", "2"));

    System.out.println("After initializing list");
    for (Tester t : test) {
        System.out.println(t);
    }

    Tester first = test.get(0);
    Tester second = test.get(1);
    Tester tmp = first;

    first = second;
    second = tmp;

    System.out.println("\nAfter temps");
    System.out.println(first);
    System.out.println(second);

    System.out.println("\nList changed after temps?");
    for (Tester t : test) {
        System.out.println(t);
    }

    System.out.println("\nAfter swap");
    first.swap(second);
    System.out.println(first);
    System.out.println(second);

    System.out.println("\nList after swap");
    for (Tester t : test) {
        System.out.println(t);
    }
}
}

输出:

After initializing list
1:First
2:Second

After temps
2:Second
1:First

List changed after temps?
1:First
2:Second

After swap
1:First
2:Second

List after swap
2:Second
1:First

我觉得我有点不清楚,我总是打印出第一个然后第二个(对象)所以“在交换后”应该看起来像“交换后的列表”,列表在交换本地对象后发生了变化,本地对象(再次,仅仅引用列表中的对象?)没有。

回答中的评论:当我的困惑被清除并且我的问题解决了之后,我问是否有可能将列表的实际引用提供给对象,以便在我指出它时对于其他东西,列表也会自动指向其他东西。在下面的示例中,Foo a首先指向具有属性Foo的对象A,但此引用来自list.get()。现在,如果我将其指向new Turtles(),则列表仍会指向Foo with Aenter image description here

我希望有一个“硬”参考,所以当我指向海龟时,arraylist指向乌龟。 enter image description here

这会使交换变得更容易,但我还不确定如何实现可以执行此操作的列表(如果可能的话),这将非常适合我的游戏的特定需求。

2 个答案:

答案 0 :(得分:3)

使用对象时需要注意的是保存对象的变量实际上并不代表对象。变量的值是内存中指向对象所在位置的指针。

某种类型的对象的ArrayList,通过扩展,实际上不包含对象。它包含对象引用或指向内存中对象位置的指针。

当你说:

Object a = new Object();
Object b = new Object();
a = b;

您将a设置为指向 b的值;换句话说,由于ab都指向内存中的同一个对象,因此更改另一个会改变另一个对象。

现在回答你的问题,记住这一点。如果从ArrayList中提取两个对象引用,然后交换它们,则已将引用交换为对象;但是,引用没有更改ArrayList中的位置。这解释了前三个输出。

但是,在进行内部交换后,您已经更改了存储在每个对象中的值。这意味着,尽管指向第一个和第二个对象的指针在内存中仍然相同,但它们的内部信息已被交换。

这解释了最后两个输出。

(注意,这也是您必须使用System.arraycopy而不是将int[] a设置为int[] b的原因;如果您要执行后者,则更改a会更改b。 )

答案 1 :(得分:1)

实际上你的交换方法不交换对象本身,只交换它们包含的值,字符串s按值交换,对象foo通过引用交换。

当你使用get(index)从列表中获取一个元素时,除了列表所持有的内部引用之外,你实际上得到了对该对象的引用,所以你有两个引用指向同一个对象。 / p>

要真正交换列表的元素,你应该使用List的set方法:

Tester tmp = test.get(index);
test.set(index, test.get(index+1));
test.set(index+1, tmp);

这样,元素就会在列表中进行交换,因此迭代它会打印出您期望的结果。你拥有的swap方法将会过时。