You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Child对象返回的Toy实例与构造方法传入的不是同一对象?

关于Java对象封装与防御性拷贝的疑问解答

测试用例代码

Toy t1 = new Toy(1000121, "Red Bike", 3, 98.90);
Toy t2 = new Toy(1000123, "Colouring Book", 4, 19.89);
Toy t3 = new Toy(1000128, "Skateboard", 5, 149.99);
Toy t4 = new Toy(1000127, "SpongeBob DVD", 3, 14.99);
Toy t5 = new Toy(1000130, "Bike Helmet", 1, 18.99);
Toy t6 = new Toy(1000125, "Toy car", 10, 3.99);
Toy t7 = new Toy(1000129, "Ball", 2, 5.59);
Toy t8 = new Toy(1000189, "Teddy Bear", 3, 10.79);

Toy[] listofToy1 = { t1, t2 };

Child c1 = new Child("James", 1, listofToy1);

assertTrue(c1.getChildToy().length == 2 && c1.getChildToy()[0] != t1 && c1.getChildToy()[1] != t2);
assertTrue(c1.getChildToy().length == 2 && c1.getChildToy()[0].getToyName().equals("Red Bike") && c1.getChildToy()[1].getToyName().equals("Colouring Book"));

assertTrue(c1.getChildToy().length == 2 && c1.getChildToy()[0].getToyQuantity() == 3 && c1.getChildToy()[1].getToyQuantity() == 4);

疑问点

构造方法传入了包含t1、t2的数组,为何第一个断言要求c1.getChildToy()返回的数组元素与t1、t2不是同一对象?同时第二个断言又要求返回的Toy对象属性与t1、t2一致?打印返回对象的信息,结果与t1、t2的属性完全匹配,却不理解其中原理。

核心原理:防御性拷贝(Defensive Copy)

这是面向对象编程里封装性的典型要求,目的是保护Child对象内部状态的独立性:

  • 第一个断言要求返回的不是原对象:如果直接把传入的t1、t2引用存在Child里,外部代码拿到getChildToy()返回的数组后,就能直接修改t1的属性(比如调用t1.setToyQuantity(100)),这会直接改变Child内部的玩具数据,破坏Child对象的封装性。所以必须创建原Toy对象的副本(也就是新的Toy实例,属性和原对象完全一致,但内存地址不同),这样外部修改副本不会影响Child内部的状态。
  • 第二个断言要求属性一致:副本必须和原对象的属性完全相同,这样Child才能正确保存传入的玩具信息,符合业务逻辑。

简单说就是:既要保留原数据的内容,又要切断外部对内部数据的直接修改路径。

实现思路

要满足这些断言,你需要在Child的构造方法里做两件事:

  1. 不要直接保存传入的Toy[]数组引用,而是创建一个新的数组。
  2. 对数组里的每个Toy对象,调用拷贝构造方法(或者手动创建新的Toy实例,把原对象的属性值一一传入),把副本存入新数组。
  3. getChildToy()方法也要返回内部数组的副本,而不是直接返回内部保存的数组引用(防止外部修改数组结构)。

举个Child构造方法的示例片段:

public Child(String name, int id, Toy[] toys) {
    // 创建新数组,长度和传入数组一致
    this.toys = new Toy[toys.length];
    for (int i = 0; i < toys.length; i++) {
        // 假设Toy有拷贝构造方法
        this.toys[i] = new Toy(toys[i]);
        // 如果没有拷贝构造,就手动复制属性:
        // this.toys[i] = new Toy(toys[i].getToyId(), toys[i].getToyName(), toys[i].getToyQuantity(), toys[i].getPrice());
    }
}

public Toy[] getChildToy() {
    // 返回内部数组的副本,防止外部修改数组长度或替换元素
    Toy[] copy = new Toy[this.toys.length];
    System.arraycopy(this.toys, 0, copy, 0, this.toys.length);
    return copy;
}

内容的提问来源于stack exchange,提问作者Ethan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 00:47:28