说明:以下习题围绕今日授课核心内容——内部类(Inner Class) 展开,涵盖成员内部类、私有内部类的设计原则、静态成员内部类、局部内部类等知识点。
题目分为两部分:随堂练习(内部类基础,4 题)和 进阶练习(综合挑战,2 题)。
以下练习围绕内部类的基础概念和用法,由浅入深。
难度:⭐
知识点:内部类概念、成员内部类定义、内部类访问外部类成员
场景描述:汽车(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 内部类中访问它,观察是否能编译通过。以下为综合挑战题,融合多种内部类类型、内部类访问规则、设计原则等知识点。
模拟计算机(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() 还能编译通过吗?| 序号 | 所属部分 | 核心知识点 | 难度 |
|---|---|---|---|
| 练习 1 | 随堂 | 成员内部类概念、内部类访问外部类成员 | ⭐ |
| 练习 2 | 随堂 | 私有内部类设计原则、外部类提供公共方法暴露内部类功能 | ⭐⭐ |
| 练习 3 | 随堂 | 静态成员内部类、创建格式、与外部类的关系 | ⭐⭐ |
| 练习 4 | 随堂 | 局部内部类、定义在方法内部、作用域限制 | ⭐⭐⭐ |
| 进阶 1 | 进阶 | 成员内部类 + 静态内部类 + 私有内部类综合运用 | ⭐⭐⭐⭐ |
| 进阶 2 | 进阶 | 私有内部类 + 局部内部类 + 静态内部类 + 设计模式综合 | ⭐⭐⭐⭐⭐ |