Mr.Mou @ ShiShi AP Center

AP CSA 3.6|BlueJ 小练习:谁改了 Alex 的地址?

Who Changed My Address? Alex 的地址明明是在创建对象时复制的,为什么后来还是会被别人修改?用 BlueJ 的 Object Bench 亲自找出原因。

AP CSA 3.6 · Object References 约 15 分钟 Predict → Test → Explain → Fix

0 · 准备两个 Class(3 min)

在 BlueJ 新建项目 ReferenceLab,分别创建 Address 和 Person,复制代码后 Compile。

Address.java

public class Address
{
    private String city;

    public Address(String c)
    {
        city = c;
    }

    // Copy constructor
    public Address(Address other)
    {
        city = other.getCity();
    }

    public String getCity()
    {
        return city;
    }

    public void setCity(String c)
    {
        city = c;
    }
}

Person.java

public class Person
{
    private String name;
    private Address addr;

    public Person(String n, Address a)
    {
        name = n;
        addr = new Address(a);
    }

    public String getCity()
    {
        return addr.getCity();
    }

    public Address getAddress()
    {
        return addr;
    }
}

观察重点:每出现一次 new,就画一个新 object;每出现一次 reference 赋值或返回,就画一条箭头。

1 · Experiment A:改变 home(4 min)

  1. 在 Object Bench 创建 new Address("Chengdu"),命名为 home。
  2. 创建 new Person("Alex", home),命名为 alex。
  3. 先预测:修改 home 后,Alex 会住在哪里?
  4. 右键 home → setCity("Shanghai");再右键 alex → getCity()。

Predict: alex.getCity() → Chengdu / Shanghai ?

查看结果与 Reference 图

Chengdu. Person constructor 中的 new Address(a) 创建了独立的地址对象(defensive copy)。

home      ──► Address #1: Shanghai
alex.addr ──► Address #2: Chengdu

2 · Experiment B:改变 returned(4 min)

  1. 右键 alex → getAddress()。
  2. 在返回对象的窗口中点击 Get,把它放到 Object Bench,命名为 returned。
  3. 先预测:改变 returned 会影响 Alex 吗?
  4. 右键 returned → setCity("Beijing");再调用 alex.getCity()。

Predict: alex.getCity() → Chengdu / Beijing ?

查看结果与 Reference 图

Beijing! getAddress() 直接 return addr;,所以 returned 与 alex.addr 指向同一个对象(aliasing)。

alex.addr ──┐
            ├──► Address #2: Beijing
returned ───┘

即使 addr 是 private,返回其 reference 仍可能允许外部代码修改内部对象。

3 · Fix it:只改一行(3 min)

任务:只修改 Person.getAddress() 中的一行,让返回的地址成为独立 object。

public Address getAddress()
{
    return ____________;
}

改完后 Compile,重新创建 home、alex 和 returned,再重复 Experiment B。旧 Object Bench 中的对象不要继续使用。

查看修复代码
public Address getAddress()
{
    return new Address(addr);
}

现在修改 returned 不会影响 alex,因为 getter 返回了新的 Address object。

4 · Exit Ticket(1 min)

不用运行代码,直接预测:

Address a = new Address("Chengdu");
Address b = a;
Address c = new Address(a);
b.setCity("Tokyo");

Questions: ① 创建了几个 Address objects?② a、b、c 各自的城市是什么?

查看答案

2 个 objects。 a 和 b 都是 Tokyo;c 仍然是 Chengdu。