Menu
CoddyTech

聚合与组合

本课属于 Coddy Java 学习旅程的“面向对象编程”单元。第 48 课,共 87 课。

聚合和组合都是“拥有”关系,但它们在一个关键方面有所不同:所有权和生命周期依赖关系。

在组合关系中,被包含的对象不能脱离容器独立存在。容器销毁时,其中的组成部分也会随之销毁。想想House和它的Room对象:没有房子,房间就无法存在:

class House {
    private Room[] rooms;
    
    public House(int numRooms) {
        rooms = new Room[numRooms];
        for (int i = 0; i < numRooms; i++) {
            rooms[i] = new Room();  // House 创建并拥有这些房间
        }
    }
}

在聚合中,被包含的对象可以独立存在。容器会使用该对象,但不会控制它的生命周期。以 Team 和它的 Player 对象为例:即使没有球队,球员也可以存在:

class Team {
    private List<Player> players;
    
    public Team() {
        players = new ArrayList<>();
    }
    
    public void addPlayer(Player player) {
        players.add(player);  // Team 使用已有的玩家
    }
}

关键区别:在组合中,容器在内部创建其组成部分。在聚合中,对象从外部传入。删除 Team 时,Player 对象仍然存在。删除 House 时,其中的 Room 对象也会消失。

challenge icon

挑战

简单

我们来构建一个大学课程系统,演示聚合和组合。你会看到,Course拥有自己的Lecture对象(组合),而它只引用独立存在的Professor对象(聚合)。

你要把代码分成四个文件:

  • Professor.java:创建一个表示教授的类,这个教授可以独立于任何课程存在。Professor 有两个私有字段:name(String)和department(String)。添加一个构造函数来初始化这两个字段,并为每个字段添加 getter 方法。重写toString(),使其返回:Prof. [name] ([department])
  • Lecture.java:创建一个表示讲座的类,这种讲座只有在课程中才有意义。Lecture 有两个私有字段:topic(String)和durationMinutes(int)。添加一个构造函数和 getter 方法。重写toString(),使其返回:Lecture: [topic] ([durationMinutes] min)
  • Course.java:在这里演示这两种关系!Course 有三个私有字段:title(String)、professor(Professor,聚合)和lectures(Lecture 对象数组,组合)。

    构造函数接收课程标题、一个 Professor 对象(从外部传入,聚合)以及讲座数量。在构造函数内部,创建 Lecture 数组,并用新的 Lecture 对象填充数组。对于索引为i的每个讲座,使用主题"Topic " + (i + 1)和时长45分钟来创建它。这种内部创建体现了组合。

    添加一个getInfo()方法,返回多行 String:

    Course: [title]
    Instructor: [professor.toString()]
    Lectures: [number of lectures]

    还要添加一个listLectures()方法,返回一个 String,其中每个讲座的 toString() 都单独占一行。

  • Main.java:把所有内容组合起来!你会收到四项输入:教授姓名(String)、教授所属院系(String)、课程标题(String)和讲座数量(int)。

    首先,使用给定的姓名和院系创建一个 Professor。这个教授独立存在。然后创建一个 Course,传入课程标题、教授(聚合)和讲座数量(课程会在内部创建自己的讲座,也就是组合)。

    输出getInfo()的结果,然后输出一个空行,再输出listLectures()的结果。

你会按顺序收到四项输入:教授姓名、院系、课程标题和讲座数量。

请注意关键区别:Professor 在外部创建并传入 Course(聚合:这位教授可以教授其他课程,也可以在没有这门课程的情况下独立存在)。Lecture 在 Course 构造函数内部创建(组合。它们只属于这门课程,没有这门课程就不会存在)。

亲手试试

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 读取输入
        String professorName = scanner.nextLine();
        String department = scanner.nextLine();
        String courseTitle = scanner.nextLine();
        int numberOfLectures = scanner.nextInt();
        
        // TODO:创建一个 Professor 对象(可独立存在,属于聚合关系)
        
        // TODO:创建一个 Course 对象,并传入 professor(聚合关系)
        // 课程会在内部创建自己的讲座(组合关系)
        
        // TODO:输出 getInfo() 的结果
        
        // TODO:输出一个空行
        
        // TODO:输出 listLectures() 的结果
        
    }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Java 在线编译器