|
@@ -1,8 +1,8 @@
|
|
|
# 2026-07-27 内部类(Inner Class)基础练习(课堂练习用)
|
|
# 2026-07-27 内部类(Inner Class)基础练习(课堂练习用)
|
|
|
|
|
|
|
|
-> **说明**:以下习题围绕今日授课核心内容——**内部类(Inner Class)** 展开,涵盖成员内部类、私有内部类的设计原则、静态成员内部类、局部内部类等知识点。
|
|
|
|
|
|
|
+> **说明**:以下习题围绕今日授课核心内容——**内部类(Inner Class)** 展开,涵盖成员内部类、私有内部类的设计原则、静态成员内部类、局部内部类、匿名内部类等知识点。
|
|
|
>
|
|
>
|
|
|
-> 题目分为两部分:**随堂练习**(内部类基础,4 题)和 **进阶练习**(综合挑战,2 题)。
|
|
|
|
|
|
|
+> 题目分为两部分:**随堂练习**(内部类基础,7 题)和 **进阶练习**(综合挑战,3 题)。
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
@@ -241,6 +241,312 @@ public class RestaurantTest {
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
|
|
+## 练习 5:匿名内部类入门——多态接收方式(USB 设备 + 计算器)
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐⭐
|
|
|
|
|
+**知识点**:匿名内部类概念、使用前提、语法格式、多态接收(变量赋值)
|
|
|
|
|
+
|
|
|
|
|
+**场景描述**:不使用独立的实现类,直接通过匿名内部类创建 USB 设备和计算器对象。匿名内部类的本质是 **继承了某个类或实现了某个接口的子类的匿名对象**,通过多态接收赋值给父类/接口类型的变量。
|
|
|
|
|
+
|
|
|
|
|
+### 5.1 基于接口的匿名内部类(Usb)
|
|
|
|
|
+
|
|
|
|
|
+参考课堂 `Usb` 接口和 `Demo01.java`,使用匿名内部类创建 USB 设备。
|
|
|
|
|
+
|
|
|
|
|
+**`Usb` 接口**(复用课堂代码):
|
|
|
|
|
+- **抽象方法**:`void startUsb()` —— 启动设备
|
|
|
|
|
+- **抽象方法**:`void closeUsb()` —— 关闭设备
|
|
|
|
|
+
|
|
|
|
|
+**要求**:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class DemoTest {
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ // 1. 使用匿名内部类创建 Usb 对象(鼠标)
|
|
|
|
|
+ // 提示:Usb usb = new Usb() { ... };
|
|
|
|
|
+ // 请补充代码:
|
|
|
|
|
+ Usb usb = ;
|
|
|
|
|
+ usb.startUsb();
|
|
|
|
|
+ usb.closeUsb();
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println();
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 再创建一个 Usb 匿名内部类对象(键盘)
|
|
|
|
|
+ // 输出:"键盘已连接,USB 接口启动"
|
|
|
|
|
+ // 输出:"键盘已断开,USB 接口关闭"
|
|
|
|
|
+ // 请补充代码:
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**:
|
|
|
|
|
+```
|
|
|
|
|
+鼠标已连接,USB 接口启动
|
|
|
|
|
+鼠标已断开,USB 接口关闭
|
|
|
|
|
+
|
|
|
|
|
+键盘已连接,USB 接口启动
|
|
|
|
|
+键盘已断开,USB 接口关闭
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5.2 基于抽象类的匿名内部类(Compute)
|
|
|
|
|
+
|
|
|
|
|
+参考课堂 `Compute` 抽象类,使用匿名内部类实现加法计算器。
|
|
|
|
|
+
|
|
|
|
|
+**`Compute` 抽象类**(复用课堂代码):
|
|
|
|
|
+- **抽象方法**:`int add(int a, int b)` —— 两数相加
|
|
|
|
|
+
|
|
|
|
|
+**要求**:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+// 使用匿名内部类创建 Compute 对象,实现加法计算
|
|
|
|
|
+Compute compute = new Compute() {
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+};
|
|
|
|
|
+int result = compute.add(10, 20);
|
|
|
|
|
+System.out.println("10 + 20 = " + result);
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**:
|
|
|
|
|
+```
|
|
|
|
|
+10 + 20 = 30
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 思考题
|
|
|
|
|
+
|
|
|
|
|
+1. 匿名内部类的前提条件是什么?为什么必须存在一个类或接口?
|
|
|
|
|
+2. 匿名内部类的本质是什么?它的 `.class` 文件名会是什么样子的?
|
|
|
|
|
+3. 语法格式中最后的分号 `;` 能否省略?为什么?
|
|
|
|
|
+4. 匿名内部类和之前学的局部内部类有什么区别?
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 练习 6:匿名内部类做方法参数——餐具 + USB 设备(开发中最常用方式)
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐⭐⭐
|
|
|
|
|
+**知识点**:匿名内部类作为方法参数传递、减少独立实现类文件、开发中最常用
|
|
|
|
|
+
|
|
|
|
|
+**场景描述**:在开发中,如果某个接口的实现类只想在当前场景使用一次,不想被其他人复用,可以直接在方法参数位置创建匿名内部类,避免创建独立的实现类文件。本练习通过「餐具使用」和「USB 设备接入」两个场景来练习这种最常用的方式。
|
|
|
|
|
+
|
|
|
|
|
+### 6.1 Person 吃饭使用餐具
|
|
|
|
|
+
|
|
|
|
|
+参考课堂 `Tableware` 接口、`Person` 类和 `Test02.java`。
|
|
|
|
|
+
|
|
|
|
|
+**`Tableware` 接口**(复用课堂代码):
|
|
|
|
|
+- **抽象方法**:`void openTableware()` —— 打开餐具
|
|
|
|
|
+- **抽象方法**:`void useTableware()` —— 使用餐具
|
|
|
|
|
+
|
|
|
|
|
+**`Person` 类**(复用课堂代码):
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class Person {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 吃饭
|
|
|
|
|
+ * @param tableware 吃饭使用的餐具
|
|
|
|
|
+ */
|
|
|
|
|
+ public void eat(Tableware tableware) {
|
|
|
|
|
+ tableware.openTableware();
|
|
|
|
|
+ tableware.useTableware();
|
|
|
|
|
+ System.out.println("开始干饭......");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**要求**:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class EatTest {
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ Person person = new Person();
|
|
|
|
|
+
|
|
|
|
|
+ // 方式一:先创建匿名内部类对象,再传入方法
|
|
|
|
|
+ // 请补充代码:创建 Tableware 的匿名内部类(一次性筷子)
|
|
|
|
|
+ Tableware tableware = ; // 补全
|
|
|
|
|
+ person.eat(tableware);
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println();
|
|
|
|
|
+
|
|
|
|
|
+ // 方式二(推荐):直接在方法参数中创建匿名内部类(一次性勺子)
|
|
|
|
|
+ // 这种写法更紧凑,开发中最常用!
|
|
|
|
|
+ person.eat(/* 请在此位置直接创建 Tableware 的匿名内部类 */);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**:
|
|
|
|
|
+```
|
|
|
|
|
+打开一次性筷子
|
|
|
|
|
+使用一次性筷子
|
|
|
|
|
+开始干饭......
|
|
|
|
|
+
|
|
|
|
|
+打开一次性勺子
|
|
|
|
|
+使用一次性勺子
|
|
|
|
|
+开始干饭......
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 6.2 NoteBook 接入 USB 设备
|
|
|
|
|
+
|
|
|
|
|
+参考课堂 `NoteBook` 类和 `Test01.java`。
|
|
|
|
|
+
|
|
|
|
|
+**`NoteBook` 类**(复用课堂代码):
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class NoteBook {
|
|
|
|
|
+ public void useUSB(Usb usb) {
|
|
|
|
|
+ usb.startUsb();
|
|
|
|
|
+ usb.closeUsb();
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**要求**:不创建任何独立的实现类,完全使用匿名内部类完成以下操作。
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class NoteBookTest {
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ NoteBook nb = new NoteBook();
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 使用方式一:多态接收 + 传入方法
|
|
|
|
|
+ // 创建 Usb 匿名内部类(U盘)
|
|
|
|
|
+ // 输出:"U盘已插入,正在读取数据..."
|
|
|
|
|
+ // 输出:"U盘已安全弹出"
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 使用方式二(推荐):直接作为参数传递
|
|
|
|
|
+ // 直接在 useUSB() 参数中创建 Usb 匿名内部类(移动硬盘)
|
|
|
|
|
+ // 输出:"移动硬盘已连接,传输速率 5Gbps"
|
|
|
|
|
+ // 输出:"移动硬盘已断开连接"
|
|
|
|
|
+ nb.useUSB(/* 请在此直接创建匿名内部类 */);
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println();
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 尝试接入第三个设备(摄像头),同样使用匿名内部类
|
|
|
|
|
+ nb.useUSB(/* 请在此直接创建匿名内部类 */);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**预期输出**:
|
|
|
|
|
+```
|
|
|
|
|
+U盘已插入,正在读取数据...
|
|
|
|
|
+U盘已安全弹出
|
|
|
|
|
+
|
|
|
|
|
+移动硬盘已连接,传输速率 5Gbps
|
|
|
|
|
+移动硬盘已断开连接
|
|
|
|
|
+
|
|
|
|
|
+摄像头已启动,正在捕获画面...
|
|
|
|
|
+摄像头已关闭,停止录制
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 思考题
|
|
|
|
|
+
|
|
|
|
|
+1. 使用匿名内部类作为方法参数有什么好处?(对比先创建独立实现类再传入的方式)
|
|
|
|
|
+2. 什么场景下适合使用匿名内部类?什么场景下应该创建独立的实现类?
|
|
|
|
|
+3. 如果 `Tableware` 接口有 5 个抽象方法,使用匿名内部类实现时有何感受?这说明了什么设计原则?
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 练习 7:匿名内部类三种使用方式对比——多功能计算器
|
|
|
|
|
+
|
|
|
|
|
+**难度**:⭐⭐⭐
|
|
|
|
|
+**知识点**:匿名内部类的三种使用方式对比、匿名对象直接调用、特有方法
|
|
|
|
|
+
|
|
|
|
|
+**场景描述**:通过一个多功能计算器案例,对比匿名内部类的三种使用方式。理解它们各自的适用场景和优缺点。
|
|
|
|
|
+
|
|
|
|
|
+**`Calculator` 接口**:
|
|
|
|
|
+- **抽象方法**:`int add(int a, int b)` —— 加法
|
|
|
|
|
+- **抽象方法**:`int subtract(int a, int b)` —— 减法
|
|
|
|
|
+
|
|
|
|
|
+**`AdvCalculator` 抽象类**:
|
|
|
|
|
+- **抽象方法**:`int multiply(int a, int b)` —— 乘法
|
|
|
|
|
+- **抽象方法**:`int divide(int a, int b)` —— 除法
|
|
|
|
|
+- **普通方法**:`void showInfo()` —— 输出 `"高级计算器"`
|
|
|
|
|
+- **特有方法**:`void reset()` —— 输出 `"计算器已重置"`(注意:这是一个普通方法,不是抽象方法)
|
|
|
|
|
+
|
|
|
|
|
+**要求**:完成以下测试类,对比三种方式。
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class CalculatorTest {
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ // ============ 方式一:多态接收(变量赋值) ============
|
|
|
|
|
+ // 特点:变量可重复使用,但只能调用父类/接口中声明的方法
|
|
|
|
|
+ // 要求:创建 Calculator 接口的匿名内部类,实现加法和减法
|
|
|
|
|
+
|
|
|
|
|
+ Calculator calc = new Calculator() {
|
|
|
|
|
+ // 请补充实现
|
|
|
|
|
+ };
|
|
|
|
|
+ System.out.println("加法:" + calc.add(30, 12)); // 42
|
|
|
|
|
+ System.out.println("减法:" + calc.subtract(30, 12)); // 18
|
|
|
|
|
+ // calc.reset(); // ❌ 思考:这行能编译通过吗?为什么?
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println();
|
|
|
|
|
+
|
|
|
|
|
+ // ============ 方式二:作为方法参数传递 ============
|
|
|
|
|
+ // 特点:最常用,代码紧凑,适合一次性使用
|
|
|
|
|
+ // 要求:定义一个 useCalculator 方法,接收 Calculator 参数,
|
|
|
|
|
+ // 调用 add 和 subtract 并输出结果
|
|
|
|
|
+
|
|
|
|
|
+ useCalculator(new Calculator() {
|
|
|
|
|
+ // 请补充实现(计算 100 + 200 和 100 - 200)
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println();
|
|
|
|
|
+
|
|
|
|
|
+ // ============ 方式三:匿名对象直接调用 ============
|
|
|
|
|
+ // 特点:可以调用匿名内部类中定义的特有方法
|
|
|
|
|
+ // 要求:创建 AdvCalculator 抽象类的匿名内部类,并直接调用方法
|
|
|
|
|
+
|
|
|
|
|
+ // 在匿名内部类中重写 multiply() 和 divide()
|
|
|
|
|
+ // 同时定义一个特有方法:void showMemory() —— 输出 "内存已清空"
|
|
|
|
|
+ // 然后通过匿名对象直接调用 multiply()、divide()、showMemory()
|
|
|
|
|
+
|
|
|
|
|
+ new AdvCalculator() {
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+ }. /* 直接调用方法 */;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 方式二的辅助方法
|
|
|
|
|
+ public static void useCalculator(Calculator calc) {
|
|
|
|
|
+ System.out.println("使用计算器:");
|
|
|
|
|
+ System.out.println("加法结果:" + calc.add(100, 200));
|
|
|
|
|
+ System.out.println("减法结果:" + calc.subtract(100, 200));
|
|
|
|
|
+ System.out.println("计算完毕!");
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 预期输出
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+============ 方式一:多态接收 ============
|
|
|
|
|
+加法:42
|
|
|
|
|
+减法:18
|
|
|
|
|
+
|
|
|
|
|
+============ 方式二:方法参数 ============
|
|
|
|
|
+使用计算器:
|
|
|
|
|
+加法结果:300
|
|
|
|
|
+减法结果:-100
|
|
|
|
|
+计算完毕!
|
|
|
|
|
+
|
|
|
|
|
+============ 方式三:匿名对象直接调用 ============
|
|
|
|
|
+乘法:15 x 8 = 120
|
|
|
|
|
+除法:15 ÷ 8 = 1
|
|
|
|
|
+内存已清空
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 对比总结填空
|
|
|
|
|
+
|
|
|
|
|
+请根据以上三种方式,完成下面的对比表格:
|
|
|
|
|
+
|
|
|
|
|
+| 对比维度 | 方式一:多态接收 | 方式二:方法参数 | 方式三:匿名对象直接调用 |
|
|
|
|
|
+|---------|----------------|----------------|----------------------|
|
|
|
|
|
+| 能否重复使用 | ①______ | 不能(一次性) | ②______ |
|
|
|
|
|
+| 能否调用特有方法 | ③______ | ④______ | ⑤______ |
|
|
|
|
|
+| 开发中使用频率 | 较少 | ⑥______ | 极少 |
|
|
|
|
|
+| 典型代码结构 | `接口 变量 = new 接口(){}.方法` | `方法名(new 接口(){})` | `new 接口(){}.方法()` |
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
# 第二部分:进阶练习
|
|
# 第二部分:进阶练习
|
|
|
|
|
|
|
|
> 以下为综合挑战题,融合多种内部类类型、内部类访问规则、设计原则等知识点。
|
|
> 以下为综合挑战题,融合多种内部类类型、内部类访问规则、设计原则等知识点。
|
|
@@ -489,6 +795,196 @@ public class BankTest {
|
|
|
|
|
|
|
|
---
|
|
---
|
|
|
|
|
|
|
|
|
|
+## 第 3 题:综合应用——学生信息管理系统(匿名内部类 + 排序 + 筛选 + 接口回调)⭐⭐⭐⭐⭐
|
|
|
|
|
+
|
|
|
|
|
+### 需求说明
|
|
|
|
|
+
|
|
|
|
|
+开发一个学生信息管理系统,使用 `Student` 实体类,通过 **匿名内部类** 实现多种排序和筛选逻辑。重点练习匿名内部类作为方法参数传递的典型应用(策略模式雏形)。
|
|
|
|
|
+
|
|
|
|
|
+要求:
|
|
|
|
|
+- **不创建任何 StudentComparator 或 StudentFilter 的实现类**
|
|
|
|
|
+- 所有比较逻辑和筛选逻辑全部使用 **匿名内部类** 实现
|
|
|
|
|
+- 体验「行为参数化」的思想——将行为(排序/筛选)作为参数传递给方法
|
|
|
|
|
+
|
|
|
|
|
+### 类设计
|
|
|
|
|
+
|
|
|
|
|
+**`Student` 类**(参考课堂 Student 类,增加 `score` 属性):
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class Student {
|
|
|
|
|
+ private String id; // 学号
|
|
|
|
|
+ private String name; // 姓名
|
|
|
|
|
+ private Integer age; // 年龄
|
|
|
|
|
+ private Double score; // 成绩
|
|
|
|
|
+
|
|
|
|
|
+ // 构造方法:Student(String id, String name, Integer age, Double score)
|
|
|
|
|
+ // getter/setter
|
|
|
|
|
+ // toString() 返回格式:Student{id='xxx', name='xxx', age=xx, score=xx}
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**需要自行定义的接口**:
|
|
|
|
|
+
|
|
|
|
|
+**`Comparator` 接口**(比较器):
|
|
|
|
|
+- **抽象方法**:`int compare(Student s1, Student s2)`
|
|
|
|
|
+- 返回负数表示 s1 < s2,返回正数表示 s1 > s2,返回 0 表示相等
|
|
|
|
|
+
|
|
|
|
|
+**`Filter` 接口**(筛选器):
|
|
|
|
|
+- **抽象方法**:`boolean accept(Student student)`
|
|
|
|
|
+- 返回 `true` 表示保留该学生,`false` 表示过滤掉
|
|
|
|
|
+
|
|
|
|
|
+**`StudentManager` 类**(业务类,由学生自行实现):
|
|
|
|
|
+
|
|
|
|
|
+| 方法 | 说明 |
|
|
|
|
|
+|------|------|
|
|
|
|
|
+| `void sortStudents(Student[] students, Comparator comparator)` | 使用冒泡排序按指定比较器对学生数组排序 |
|
|
|
|
|
+| `void printStudents(Student[] students)` | 遍历输出所有学生信息 |
|
|
|
|
|
+| `Student[] filterStudents(Student[] students, Filter filter)` | 按指定条件筛选学生,返回筛选后的新数组 |
|
|
|
|
|
+| `Student findMax(Student[] students, Comparator comparator)` | 按指定比较器查找「最大」学生 |
|
|
|
|
|
+
|
|
|
|
|
+### 测试类要求
|
|
|
|
|
+
|
|
|
|
|
+**`StudentSystemTest` 类**:创建至少 6 个学生对象,放入数组,然后使用匿名内部类完成以下操作:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public class StudentSystemTest {
|
|
|
|
|
+ public static void main(String[] args) {
|
|
|
|
|
+ // 1. 创建学生数组
|
|
|
|
|
+ Student[] students = new Student[]{
|
|
|
|
|
+ new Student("S001", "张三", 20, 85.5),
|
|
|
|
|
+ new Student("S002", "李四", 22, 92.0),
|
|
|
|
|
+ new Student("S003", "王五", 19, 76.5),
|
|
|
|
|
+ new Student("S004", "赵六", 21, 88.0),
|
|
|
|
|
+ new Student("S005", "孙七", 23, 95.5),
|
|
|
|
|
+ new Student("S006", "周八", 20, 68.0)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ StudentManager manager = new StudentManager();
|
|
|
|
|
+
|
|
|
|
|
+ System.out.println("========== 原始学生信息 ==========");
|
|
|
|
|
+ manager.printStudents(students);
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 任务 1:按成绩从高到低排序 ==========
|
|
|
|
|
+ // 使用匿名内部类实现 Comparator 接口(成绩降序)
|
|
|
|
|
+ manager.sortStudents(students, new Comparator() {
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+ });
|
|
|
|
|
+ System.out.println("\n========== 任务 1:按成绩从高到低排序 ==========");
|
|
|
|
|
+ manager.printStudents(students);
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 任务 2:按年龄从小到大排序 ==========
|
|
|
|
|
+ // 使用匿名内部类实现 Comparator 接口(年龄升序)
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 任务 3:按姓名长度从短到长排序 ==========
|
|
|
|
|
+ // 提示:s1.getName().length() - s2.getName().length()
|
|
|
|
|
+ // 使用匿名内部类实现 Comparator 接口
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 任务 4:筛选成绩 >= 85 的优秀学生 ==========
|
|
|
|
|
+ // 使用匿名内部类实现 Filter 接口
|
|
|
|
|
+ Student[] excellentStudents = manager.filterStudents(students, new Filter() {
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+ });
|
|
|
|
|
+ System.out.println("\n========== 任务 4:筛选成绩 >= 85 的优秀学生 ==========");
|
|
|
|
|
+ manager.printStudents(excellentStudents);
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 任务 5:筛选年龄 > 20 的学生 ==========
|
|
|
|
|
+ // 使用匿名内部类实现 Filter 接口
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 任务 6:查找成绩最高的学生 ==========
|
|
|
|
|
+ // 使用匿名内部类实现 Comparator 接口,配合 findMax() 方法
|
|
|
|
|
+ Student topStudent = manager.findMax(students, new Comparator() {
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+ });
|
|
|
|
|
+ System.out.println("\n========== 任务 6:成绩最高的学生 ==========");
|
|
|
|
|
+ System.out.println(topStudent);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 预期输出(参考)
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+========== 原始学生信息 ==========
|
|
|
|
|
+Student{id='S001', name='张三', age=20, score=85.5}
|
|
|
|
|
+Student{id='S002', name='李四', age=22, score=92.0}
|
|
|
|
|
+Student{id='S003', name='王五', age=19, score=76.5}
|
|
|
|
|
+Student{id='S004', name='赵六', age=21, score=88.0}
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+Student{id='S006', name='周八', age=20, score=68.0}
|
|
|
|
|
+
|
|
|
|
|
+========== 任务 1:按成绩从高到低排序 ==========
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+Student{id='S002', name='李四', age=22, score=92.0}
|
|
|
|
|
+Student{id='S004', name='赵六', age=21, score=88.0}
|
|
|
|
|
+Student{id='S001', name='张三', age=20, score=85.5}
|
|
|
|
|
+Student{id='S003', name='王五', age=19, score=76.5}
|
|
|
|
|
+Student{id='S006', name='周八', age=20, score=68.0}
|
|
|
|
|
+
|
|
|
|
|
+========== 任务 2:按年龄从小到大排序 ==========
|
|
|
|
|
+Student{id='S003', name='王五', age=19, score=76.5}
|
|
|
|
|
+Student{id='S001', name='张三', age=20, score=85.5}
|
|
|
|
|
+Student{id='S006', name='周八', age=20, score=68.0}
|
|
|
|
|
+Student{id='S004', name='赵六', age=21, score=88.0}
|
|
|
|
|
+Student{id='S002', name='李四', age=22, score=92.0}
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+
|
|
|
|
|
+========== 任务 3:按姓名长度排序 ==========
|
|
|
|
|
+Student{id='S003', name='王五', age=19, score=76.5}
|
|
|
|
|
+Student{id='S002', name='李四', age=22, score=92.0}
|
|
|
|
|
+Student{id='S001', name='张三', age=20, score=85.5}
|
|
|
|
|
+Student{id='S004', name='赵六', age=21, score=88.0}
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+Student{id='S006', name='周八', age=20, score=68.0}
|
|
|
|
|
+
|
|
|
|
|
+========== 任务 4:筛选成绩 >= 85 的优秀学生 ==========
|
|
|
|
|
+Student{id='S001', name='张三', age=20, score=85.5}
|
|
|
|
|
+Student{id='S002', name='李四', age=22, score=92.0}
|
|
|
|
|
+Student{id='S004', name='赵六', age=21, score=88.0}
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+
|
|
|
|
|
+========== 任务 5:筛选年龄 > 20 的学生 ==========
|
|
|
|
|
+Student{id='S002', name='李四', age=22, score=92.0}
|
|
|
|
|
+Student{id='S004', name='赵六', age=21, score=88.0}
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+
|
|
|
|
|
+========== 任务 6:成绩最高的学生 ==========
|
|
|
|
|
+Student{id='S005', name='孙七', age=23, score=95.5}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 代码补充挑战
|
|
|
|
|
+
|
|
|
|
|
+请补全 `StudentManager` 类中的 `sortStudents` 方法:
|
|
|
|
|
+
|
|
|
|
|
+```java
|
|
|
|
|
+public void sortStudents(Student[] students, Comparator comparator) {
|
|
|
|
|
+ // 使用冒泡排序
|
|
|
|
|
+ for (int i = 0; i < students.length - 1; i++) {
|
|
|
|
|
+ for (int j = 0; j < students.length - 1 - i; j++) {
|
|
|
|
|
+ // 如果 students[j] > students[j+1](根据 comparator 判断),则交换
|
|
|
|
|
+ // 提示:if (comparator.compare(students[j], students[j+1]) > 0) { 交换 }
|
|
|
|
|
+ // 请补充代码
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 思考题
|
|
|
|
|
+
|
|
|
|
|
+1. 本练习中,排序和筛选的逻辑是通过什么方式传入方法的?这体现了什么设计思想?
|
|
|
|
|
+2. 如果不用匿名内部类,每次排序都需要创建一个独立的实现类(如 `ScoreComparator`、`AgeComparator` 等),会有什么弊端?
|
|
|
|
|
+3. `Comparator` 接口的 `compare()` 方法返回负数、零、正数分别代表什么含义?
|
|
|
|
|
+4. 本练习的做法与 Java 标准库中的 `Comparator<T>` 接口和 `Collections.sort()` 方法有什么相似之处?
|
|
|
|
|
+5. 如果将来想按「成绩相同则按年龄排序」这种复合条件排序,需要修改哪些代码?
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
## 📋 题目总览
|
|
## 📋 题目总览
|
|
|
|
|
|
|
|
| 序号 | 所属部分 | 核心知识点 | 难度 |
|
|
| 序号 | 所属部分 | 核心知识点 | 难度 |
|
|
@@ -497,5 +993,9 @@ public class BankTest {
|
|
|
| 练习 2 | 随堂 | 私有内部类设计原则、外部类提供公共方法暴露内部类功能 | ⭐⭐ |
|
|
| 练习 2 | 随堂 | 私有内部类设计原则、外部类提供公共方法暴露内部类功能 | ⭐⭐ |
|
|
|
| 练习 3 | 随堂 | 静态成员内部类、创建格式、与外部类的关系 | ⭐⭐ |
|
|
| 练习 3 | 随堂 | 静态成员内部类、创建格式、与外部类的关系 | ⭐⭐ |
|
|
|
| 练习 4 | 随堂 | 局部内部类、定义在方法内部、作用域限制 | ⭐⭐⭐ |
|
|
| 练习 4 | 随堂 | 局部内部类、定义在方法内部、作用域限制 | ⭐⭐⭐ |
|
|
|
|
|
+| 练习 5 | 随堂 | 匿名内部类入门、多态接收方式、基于接口/抽象类 | ⭐⭐ |
|
|
|
|
|
+| 练习 6 | 随堂 | 匿名内部类作为方法参数传递(开发中最常用方式) | ⭐⭐⭐ |
|
|
|
|
|
+| 练习 7 | 随堂 | 匿名内部类三种使用方式对比、匿名对象直接调用 | ⭐⭐⭐ |
|
|
|
| 进阶 1 | 进阶 | 成员内部类 + 静态内部类 + 私有内部类综合运用 | ⭐⭐⭐⭐ |
|
|
| 进阶 1 | 进阶 | 成员内部类 + 静态内部类 + 私有内部类综合运用 | ⭐⭐⭐⭐ |
|
|
|
| 进阶 2 | 进阶 | 私有内部类 + 局部内部类 + 静态内部类 + 设计模式综合 | ⭐⭐⭐⭐⭐ |
|
|
| 进阶 2 | 进阶 | 私有内部类 + 局部内部类 + 静态内部类 + 设计模式综合 | ⭐⭐⭐⭐⭐ |
|
|
|
|
|
+| 进阶 3 | 进阶 | 匿名内部类 + 接口回调 + 排序/筛选策略模式(学生管理系统) | ⭐⭐⭐⭐⭐ |
|