说明:以下习题围绕今日授课核心内容——内部类(Inner Class) 展开,涵盖成员内部类、私有内部类的设计原则、静态成员内部类、局部内部类、匿名内部类等知识点。
题目分为两部分:随堂练习(内部类基础,7 题)和 进阶练习(综合挑战,3 题)。
以下练习围绕内部类的基础概念和用法,由浅入深。
难度:⭐
知识点:内部类概念、成员内部类定义、内部类访问外部类成员
场景描述:汽车(Car)包含发动机(Engine),发动机是汽车的内部组成部分,离开汽车无法独立存在。发动机有自己的行为(启动、熄火),同时需要知道所属汽车的信息。
Car 类(外部类):
brand(品牌,String)、model(型号,String)Car(String brand, String model)Engine:
void start() —— 输出 "发动机启动:{brand} {model} 轰鸣声..."(直接访问外部类的私有属性)void stop() —— 输出 "发动机熄火:{brand} {model} 安静了..."void drive() —— 创建 Engine 对象并调用 start(),然后输出 "{brand} {model} 正在行驶...",再调用 stop()测试类 CarTest 要求:
public class CarTest {
public static void main(String[] args) {
// 1. 创建 Car 对象
Car car = new Car("宝马", "X5");
// 2. 调用外部类方法——内部由 Car 自己完成
car.drive();
// 3. (思考)如果 Engine 不是内部类,而是独立类,
// 需要如何修改才能让 Engine 访问 Car 的 brand 和 model?
}
}
预期输出:
发动机启动:宝马 X5 轰鸣声...
宝马 X5 正在行驶...
发动机熄火:宝马 X5 安静了...
思考题:
Engine 改为私有内部类,外界(如 CarTest)能否直接创建 Engine 对象?Engine 对象的创建是在 Car 的内部方法 drive() 中完成的,这符合「外部类访问内部类必须创建对象」的特点吗?难度:⭐⭐
知识点:私有内部类、外部类提供公共方法暴露内部类功能、设计原则
场景描述:人有心脏,心脏离开人体无法独立存在。按照 私有内部类设计原则,心脏应该作为人的私有内部类,外界必须通过人的公共方法才能使用心脏的功能。
Person 类(外部类):
name(姓名,String)Person(String name)Heart:
void beat() —— 输出 "{name} 的心脏在跳动:咚咚咚..."(直接访问外部类私有属性)void rest() —— 输出 "{name} 的心脏在休息:咚...咚...咚..."void heartbeat() —— 在方法内部创建 Heart 对象并调用 beat() 和 rest()测试类 PersonTest 要求:
public class PersonTest {
public static void main(String[] args) {
// 1. 创建 Person 对象
Person person = new Person("张三");
// 2. 通过 Person 的公共方法访问心脏功能
person.heartbeat();
// 3. ❌ 思考:能直接这样创建心脏对象吗?
// Person.Heart h = new Person().new Heart(); // 编译能通过吗?为什么?
}
}
预期输出:
张三 的心脏在跳动:咚咚咚...
张三 的心脏在休息:咚...咚...咚...
请回答以下问题:
Heart 类设为 private 的好处是什么?Person 类想对外提供「获取心脏对象」的能力,应该如何修改代码?(参考课堂笔记中 getHeart() 方法的设计)Heart 写成一个独立类,让 Person 持有 Heart 成员变量,会有什么弊端?(提示:外界可能跳过 Person 直接创建 Heart 对象)代码补充挑战:请将 Person 类补充完整:
public class Person {
private String name;
// 构造方法
public Person(String name) {
this.name = name;
}
// 私有内部类 Heart
// 请补充代码
// 公共方法 heartbeat()
// 请补充代码
}
难度:⭐⭐
知识点:静态成员内部类、静态内部类创建格式、静态内部类与外部类的关系
场景描述:每所学校(School)都有统一格式的学生证(StudentCard),其中包含学校名称和学生的个人信息。学生证的模板格式由学校统一规定(静态内部类),不依赖某个具体的学校对象。
School 类(外部类):
schoolName(学校名称,static String)—— 所有学校对象共享schoolName 为 "四福科技大学"StudentCard:用 static 修饰
studentName(学生姓名,String)、studentId(学号,String)StudentCard(String studentName, String studentId)void showCard() —— 输出学生证信息(学校名称使用外部类的静态属性)测试类 SchoolTest 要求:
public class SchoolTest {
public static void main(String[] args) {
// 1. 创建静态内部类对象——格式:外部类名.内部类名 变量 = new 外部类名.内部类名(...)
School.StudentCard card1 = new School.StudentCard("张三", "2024001");
School.StudentCard card2 = new School.StudentCard("李四", "2024002");
// 2. 输出学生证信息
card1.showCard();
card2.showCard();
// 3. 思考:创建静态内部类对象时,需不需要先创建 School 对象?为什么?
}
}
预期输出:
========== 四福科技大学 学生证 ==========
姓名:张三
学号:2024001
========== 四福科技大学 学生证 ==========
姓名:李四
学号:2024002
思考题:
schoolName 不是 static 的,上面的代码能否正常运行?应该如何修改?难度:⭐⭐⭐
知识点:局部内部类、定义在方法内部的类、局部内部类的作用域
场景描述:餐厅(Restaurant)中有多种点餐方式(如堂食、外卖)。每种点餐方式的实现细节(如输出欢迎语、计算价格等)各不相同,且只在点餐方法内部使用,故设计为局部内部类。
Restaurant 类(外部类):
name(餐厅名称,String)Restaurant(String name)void dineIn() —— 堂食点餐
DineInOrder:"【堂食】xxx 餐厅欢迎您入座!"(xxx 为餐厅名称)void orderFood(String dish) —— 输出 "【堂食】已点餐:{dish},请稍候..."void pay(double amount) —— 输出 "【堂食】支付:{amount} 元,感谢惠顾!"dineIn() 方法内创建 DineInOrder 对象,调用 orderFood("红烧肉") 和 pay(68.0)void takeout() —— 外卖点餐
TakeoutOrder:"【外卖】感谢您选择 xxx 餐厅!"(xxx 为餐厅名称)void orderFood(String dish) —— 输出 "【外卖】已接单:{dish},正在准备..."void pay(double amount) —— 输出 "【外卖】在线支付:{amount} 元,等待配送..."takeout() 方法内创建 TakeoutOrder 对象,调用 orderFood("宫保鸡丁") 和 pay(38.0)测试类 RestaurantTest 要求:
public class RestaurantTest {
public static void main(String[] args) {
Restaurant restaurant = new Restaurant("幸福小馆");
// 堂食
restaurant.dineIn();
System.out.println();
// 外卖
restaurant.takeout();
// 思考:在外界能否直接创建 DineInOrder 或 TakeoutOrder 对象?为什么?
}
}
预期输出:
【堂食】幸福小馆 餐厅欢迎您入座!
【堂食】已点餐:红烧肉,请稍候...
【堂食】支付:68.0 元,感谢惠顾!
【外卖】感谢您选择 幸福小馆 餐厅!
【外卖】已接单:宫保鸡丁,正在准备...
【外卖】在线支付:38.0 元,等待配送...
思考题:
dineIn() 方法中添加一个局部变量 String welcome = "欢迎光临";,尝试在 DineInOrder 内部类中访问它,观察是否能编译通过。难度:⭐⭐
知识点:匿名内部类概念、使用前提、语法格式、多态接收(变量赋值)
场景描述:不使用独立的实现类,直接通过匿名内部类创建 USB 设备和计算器对象。匿名内部类的本质是 继承了某个类或实现了某个接口的子类的匿名对象,通过多态接收赋值给父类/接口类型的变量。
参考课堂 Usb 接口和 Demo01.java,使用匿名内部类创建 USB 设备。
Usb 接口(复用课堂代码):
void startUsb() —— 启动设备void closeUsb() —— 关闭设备要求:
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 接口关闭
参考课堂 Compute 抽象类,使用匿名内部类实现加法计算器。
Compute 抽象类(复用课堂代码):
int add(int a, int b) —— 两数相加要求:
// 使用匿名内部类创建 Compute 对象,实现加法计算
Compute compute = new Compute() {
// 请补充代码
};
int result = compute.add(10, 20);
System.out.println("10 + 20 = " + result);
预期输出:
10 + 20 = 30
.class 文件名会是什么样子的?; 能否省略?为什么?难度:⭐⭐⭐
知识点:匿名内部类作为方法参数传递、减少独立实现类文件、开发中最常用
场景描述:在开发中,如果某个接口的实现类只想在当前场景使用一次,不想被其他人复用,可以直接在方法参数位置创建匿名内部类,避免创建独立的实现类文件。本练习通过「餐具使用」和「USB 设备接入」两个场景来练习这种最常用的方式。
参考课堂 Tableware 接口、Person 类和 Test02.java。
Tableware 接口(复用课堂代码):
void openTableware() —— 打开餐具void useTableware() —— 使用餐具Person 类(复用课堂代码):
public class Person {
/**
* 吃饭
* @param tableware 吃饭使用的餐具
*/
public void eat(Tableware tableware) {
tableware.openTableware();
tableware.useTableware();
System.out.println("开始干饭......");
}
}
要求:
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 的匿名内部类 */);
}
}
预期输出:
打开一次性筷子
使用一次性筷子
开始干饭......
打开一次性勺子
使用一次性勺子
开始干饭......
参考课堂 NoteBook 类和 Test01.java。
NoteBook 类(复用课堂代码):
public class NoteBook {
public void useUSB(Usb usb) {
usb.startUsb();
usb.closeUsb();
}
}
要求:不创建任何独立的实现类,完全使用匿名内部类完成以下操作。
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
移动硬盘已断开连接
摄像头已启动,正在捕获画面...
摄像头已关闭,停止录制
Tableware 接口有 5 个抽象方法,使用匿名内部类实现时有何感受?这说明了什么设计原则?难度:⭐⭐⭐
知识点:匿名内部类的三种使用方式对比、匿名对象直接调用、特有方法
场景描述:通过一个多功能计算器案例,对比匿名内部类的三种使用方式。理解它们各自的适用场景和优缺点。
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() —— 输出 "计算器已重置"(注意:这是一个普通方法,不是抽象方法)要求:完成以下测试类,对比三种方式。
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 接口(){}.方法() |
以下为综合挑战题,融合多种内部类类型、内部类访问规则、设计原则等知识点。
模拟计算机(Computer)的组成结构。计算机包含:
Computer 类(外部类):
私有属性:
| 属性 | 类型 | 说明 |
|---|---|---|
brand |
String | 品牌 |
model |
String | 型号 |
ram |
Memory | 内存条对象(内部类) |
构造方法:Computer(String brand, String model, int ramSize) —— 初始化品牌、型号,并创建 Memory 对象(内存大小为 ramSize GB)
CPU 类(成员内部类):
cores(核心数,int)、frequency(主频,double,单位 GHz)CPU(int cores, double frequency)void process() —— 输出 "{brand} {model} CPU 正在计算:{cores} 核 {frequency}GHz 全速运行..."(访问外部类私有属性)UsbSpec 类(静态成员内部类):
USB_VERSION = "3.0"(String)MAX_SPEED = "5Gbps"(String)void showSpec() —— 输出 "USB 接口规范:版本 {USB_VERSION},最大速度 {MAX_SPEED}"void connectDevice(String deviceName) —— 输出 "已连接 {deviceName},使用 USB {USB_VERSION} 协议传输数据"Memory 类(私有成员内部类):
size(容量,int,单位 GB)Memory(int size)void loadData() —— 输出 "{brand} {model} 正在加载数据到内存({size}GB)..."| 方法 | 说明 |
|---|---|
void startUp() |
启动计算机:创建 CPU 对象(4 核,3.2GHz),调用 CPU 的 process() 方法,再调用 Memory 的 loadData() 方法 |
void replaceMemory(int newSize) |
更换内存条:重新创建 Memory 对象,输出 "内存已升级为 {newSize}GB" |
CPU getCpu() |
获取 CPU 对象(返回 CPU 类型,演示外部类返回内部类对象) |
ComputerTest 类:
public class ComputerTest {
public static void main(String[] args) {
// 1. 创建 Computer 对象(品牌:联想,型号:ThinkPad,内存:16GB)
Computer computer = new Computer("联想", "ThinkPad", 16);
// 2. 启动计算机
computer.startUp();
System.out.println();
// 3. 更换内存(升级到 32GB)
computer.replaceMemory(32);
System.out.println();
// 4. 使用静态内部类 UsbSpec
// 4.1 调用静态内部类的静态方法(无需创建对象)
Computer.UsbSpec.showSpec();
System.out.println();
// 4.2 创建静态内部类对象,调用普通方法
Computer.UsbSpec usb = new Computer.UsbSpec();
usb.connectDevice("移动硬盘");
usb.connectDevice("键盘");
System.out.println();
// 5. 通过外部类方法获取 CPU 对象(演示外部类返回内部类对象)
Computer.CPU cpu = computer.getCpu();
cpu.process();
}
}
联想 ThinkPad CPU 正在计算:4 核 3.2GHz 全速运行...
联想 ThinkPad 正在加载数据到内存(16GB)...
内存已升级为 32GB
USB 接口规范:版本 3.0,最大速度 5Gbps
已连接 移动硬盘,使用 USB 3.0 协议传输数据
已连接 键盘,使用 USB 3.0 协议传输数据
联想 ThinkPad CPU 正在计算:4 核 3.2GHz 全速运行...
UsbSpec 设计为 静态内部类?USB 接口规范和具体的计算机实例有关吗?Memory 设计为 私有内部类?外界可以直接更换内存吗?(提示:通过 replaceMemory() 方法)CPU 没有被 private 修饰,外界可以直接创建 Computer.CPU 对象吗?如果可以,创建格式是什么?UsbSpec 改为普通成员内部类(去掉 static),创建 UsbSpec 对象的格式会变成什么样?模拟银行账户系统。一个银行(Bank)管理多个账户(Account),每个账户包含交易记录(Transaction)。要求:
Bank 类(外部类):
私有静态属性:
| 属性 | 类型 | 说明 |
|---|---|---|
bankName |
String | 银行名称(静态) |
静态代码块:初始化 bankName = "四福银行"
私有成员内部类 Account:
accountId(账号,String)、holderName(户主姓名,String)、balance(余额,double)Account(String accountId, String holderName, double initialBalance)void deposit(double amount) —— 存款
DepositTransaction:"【存款交易】开始..."void execute() —— 增加余额,输出 "{holderName} 存入 {amount} 元,余额:{balance} 元(银行:{bankName})"(注意:局部内部类可访问外部类所有成员,也可访问所在方法的局部变量 amount)DepositTransaction 对象并调用 execute()void withdraw(double amount) —— 取款
"取款失败:{holderName} 余额不足(当前余额:{balance} 元)"WithdrawTransaction:"【取款交易】开始..."void execute() —— 减少余额,输出 "{holderName} 取出 {amount} 元,余额:{balance} 元"WithdrawTransaction 对象并调用 execute()方法 Account openAccount(String holderName, double initialBalance) —— 开户
"AC" + System.currentTimeMillis())Account 对象并返回"开户成功!欢迎 {holderName} 成为 {bankName} 的客户"静态内部类 RateInfo:
SAVING_RATE = 0.025(年利率 2.5%)LOAN_RATE = 0.045(贷款利率 4.5%)void showRate() —— 输出利率信息double calculateInterest(double principal, int years) —— 计算存款利息(复利:principal * Math.pow(1 + SAVING_RATE, years) - principal)BankTest 类:
public class BankTest {
public static void main(String[] args) {
// 1. 查看银行利率(静态内部类的静态方法)
Bank.RateInfo.showRate();
System.out.println();
// 2. 创建利率计算器对象(静态内部类)
Bank.RateInfo calculator = new Bank.RateInfo();
double interest = calculator.calculateInterest(10000, 3);
System.out.println("1 万元存 3 年,利息:" + String.format("%.2f", interest) + " 元");
System.out.println();
// 3. 到银行开户
Bank bank = new Bank();
// 注意:openAccount 返回的是 Bank.Account 类型(私有内部类),
// 但因为 Account 是 private 的,这里需要思考怎么接收返回值?
// 提示:可以用 var(Java 10+)或 Object 类型接收,或者让 Bank 提供其他方法来操作账户
// ❓ 问题:由于 Account 是私有内部类,BankTest 中无法声明 Bank.Account 类型的变量!
// 那么应该如何设计 Bank 类,让外界能够操作账户?
//
// 提示方案:
// 方案 A:在 Bank 中存储账户,并提供存款/取款的公共方法(通过账号索引)
// 方案 B:定义一个接口,让 Account 实现该接口,Bank 返回接口类型
//
// 请选择一种方案完成设计,并在测试类中完成以下操作:
// - 张三开户,存入 5000 元
// - 张三存款 2000 元
// - 张三取款 1000 元
// - 张三尝试取款 10000 元(余额不足)
}
}
本题的关键在于:私有内部类 Account 的类型在外界不可见,如何让外界操作账户?
请选择以下方案之一完成设计:
方案 A(推荐):在 Bank 类中使用数组或集合存储账户,对外提供 deposit(String accountId, double amount) 和 withdraw(String accountId, double amount) 等公共方法,外界通过账号来操作。
方案 B:定义一个 Accountable 接口(包含 deposit、withdraw 方法),让 Account 实现该接口,openAccount() 方法返回 Accountable 类型。
========== 四福银行 利率信息 ==========
储蓄年利率:2.5%
贷款年利率:4.5%
1 万元存 3 年,利息:768.91 元
========== 开户与交易 ==========
开户成功!欢迎 张三 成为 四福银行 的客户
【存款交易】开始...
张三 存入 5000.0 元,余额:5000.0 元(银行:四福银行)
【存款交易】开始...
张三 存入 2000.0 元,余额:7000.0 元(银行:四福银行)
【取款交易】开始...
张三 取出 1000.0 元,余额:6000.0 元
取款失败:张三 余额不足(当前余额:6000.0 元)
Account 设计为 Bank 的私有内部类?这体现了什么设计原则?(参考课堂笔记中 Heart 私有化的原因)DepositTransaction 和 WithdrawTransaction 为什么定义在方法内部,而不是定义为成员内部类?RateInfo 中的 showRate() 是静态方法,不需要创建对象就能调用;而 calculateInterest() 是普通方法,需要创建对象才能调用。这两种方式分别适用于什么场景?RateInfo 的 static 修饰,上面的代码中 Bank.RateInfo.showRate() 还能编译通过吗?new Bank.RateInfo() 还能编译通过吗?开发一个学生信息管理系统,使用 Student 实体类,通过 匿名内部类 实现多种排序和筛选逻辑。重点练习匿名内部类作为方法参数传递的典型应用(策略模式雏形)。
要求:
Student 类(参考课堂 Student 类,增加 score 属性):
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)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 个学生对象,放入数组,然后使用匿名内部类完成以下操作:
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 方法:
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) { 交换 }
// 请补充代码
}
}
}
ScoreComparator、AgeComparator 等),会有什么弊端?Comparator 接口的 compare() 方法返回负数、零、正数分别代表什么含义?Comparator<T> 接口和 Collections.sort() 方法有什么相似之处?| 序号 | 所属部分 | 核心知识点 | 难度 |
|---|---|---|---|
| 练习 1 | 随堂 | 成员内部类概念、内部类访问外部类成员 | ⭐ |
| 练习 2 | 随堂 | 私有内部类设计原则、外部类提供公共方法暴露内部类功能 | ⭐⭐ |
| 练习 3 | 随堂 | 静态成员内部类、创建格式、与外部类的关系 | ⭐⭐ |
| 练习 4 | 随堂 | 局部内部类、定义在方法内部、作用域限制 | ⭐⭐⭐ |
| 练习 5 | 随堂 | 匿名内部类入门、多态接收方式、基于接口/抽象类 | ⭐⭐ |
| 练习 6 | 随堂 | 匿名内部类作为方法参数传递(开发中最常用方式) | ⭐⭐⭐ |
| 练习 7 | 随堂 | 匿名内部类三种使用方式对比、匿名对象直接调用 | ⭐⭐⭐ |
| 进阶 1 | 进阶 | 成员内部类 + 静态内部类 + 私有内部类综合运用 | ⭐⭐⭐⭐ |
| 进阶 2 | 进阶 | 私有内部类 + 局部内部类 + 静态内部类 + 设计模式综合 | ⭐⭐⭐⭐⭐ |
| 进阶 3 | 进阶 | 匿名内部类 + 接口回调 + 排序/筛选策略模式(学生管理系统) | ⭐⭐⭐⭐⭐ |