说明:今日习题围绕 内部类(Inner Class) 深入展开,重点覆盖 静态成员内部类、局部内部类,并综合巩固成员内部类、私有内部类的设计原则。共 3 道练习,难度递进。
知识点:静态成员内部类、static 修饰内部类的特点、静态内部类的创建格式、静态内部类与外部类的关系
餐厅(Restaurant)有自己的 菜单项(MenuItem) 和 厨师(Chef)。菜单项和厨师信息可以脱离具体的餐厅实例独立存在(例如连锁餐厅采用统一标准),但它们在业务逻辑上又属于餐厅的一部分。请使用 静态成员内部类(static 修饰) 来设计这个体系。
静态内部类使用
static修饰,创建时不需要先创建外部类对象,格式为new 外部类名.内部类名(...)。
| 成员 | 说明 |
|---|---|
| 私有属性 | name(店名,String)、address(地址,String) |
| 构造方法 | Restaurant(String name, String address) |
| 方法 | void showInfo() —— 输出 "xxx(地址:xxx)" |
| 方法 | void openRestaurant() —— 输出餐厅开业信息,依次调用每个厨师和菜单项的方法 |
| 成员 | 说明 |
|---|---|
| 私有属性 | dishName(菜品名,String)、price(价格,double) |
| 构造方法 | MenuItem(String dishName, double price) |
| 方法 | void showDish() —— 输出 "xxx —— ¥xxx" |
| 方法 | void describe() —— 输出 "这是 xxx,价格 ¥xxx,欢迎品尝" |
| 成员 | 说明 |
|---|---|
| 私有属性 | name(姓名,String)、specialty(拿手菜,String) |
| 构造方法 | Chef(String name, String specialty) |
| 方法 | void cook() —— 输出 "厨师 xxx 正在烹饪拿手菜:xxx" |
| 方法 | void showInfo() —— 输出 "厨师:xxx \| 拿手菜:xxx" |
TestRestaurant 类:
直接创建菜单项对象(不依赖 Restaurant 实例):
Restaurant.MenuItem item1 = new Restaurant.MenuItem("宫保鸡丁", 38.0);
Restaurant.MenuItem item2 = new Restaurant.MenuItem("麻婆豆腐", 28.0);
静态内部类的创建格式:
外部类名.内部类名 对象名 = new 外部类名.内部类名(...)
直接创建厨师对象(不依赖 Restaurant 实例):
Restaurant.Chef chef1 = new Restaurant.Chef("王师傅", "宫保鸡丁");
Restaurant.Chef chef2 = new Restaurant.Chef("李师傅", "麻婆豆腐");
分别调用菜单项和厨师的方法展示信息
创建 Restaurant 对象("川味轩", "北京市海淀区"),调用 openRestaurant()
尝试:在 Chef 的 cook() 方法中直接访问外部类的 name 属性——观察是否编译报错,理解静态内部类的访问限制
静态内部类和普通成员内部类在 创建方式 上有什么区别?从 new 的语法角度具体说明。
静态内部类能否直接访问外部类的 实例成员(如 name、address)?为什么?
如果确实需要在静态内部类中访问外部类的实例成员,应该如何改造代码?(提示:通过参数将外部类引用传入静态内部类)
在这个场景中,MenuItem 和 Chef 设计为静态内部类是否合理?如果改为普通成员内部类,会对创建方式产生什么影响?
========== 菜单信息 ==========
宫保鸡丁 —— ¥38.0
这是 宫保鸡丁,价格 ¥38.0,欢迎品尝
麻婆豆腐 —— ¥28.0
这是 麻婆豆腐,价格 ¥28.0,欢迎品尝
========== 厨师信息 ==========
厨师:王师傅 | 拿手菜:宫保鸡丁
厨师:李师傅 | 拿手菜:麻婆豆腐
========== 餐厅开业 ==========
川味轩(地址:北京市海淀区)开业啦!
厨师 王师傅 正在烹饪拿手菜:宫保鸡丁
厨师 李师傅 正在烹饪拿手菜:麻婆豆腐
欢迎莅临品尝!
知识点:局部内部类、局部内部类的作用域、局部内部类访问方法参数和局部变量、effectively final
在电商购物车结算时,我们需要计算商品的总价、折扣价等。计算逻辑只在该次结算方法中使用,不需要对外暴露,因此非常适合设计为 局部内部类。
| 成员 | 说明 |
|---|---|
| 私有属性 | items(商品名称数组,String[]) |
| 私有属性 | prices(单价数组,double[]) |
| 私有属性 | quantities(数量数组,int[]) |
| 构造方法 | ShoppingCart(String[] items, double[] prices, int[] quantities) —— 三个数组长度应一致 |
| 方法 | void checkout(double discountRate) —— 结算方法,内部定义局部内部类 完成计算 |
| 方法 | void showCart() —— 显示购物车中的所有商品信息 |
OrderCalculator在 checkout(double discountRate) 方法内部定义以下局部内部类:
| 成员 | 说明 |
|---|---|
| 构造方法 | OrderCalculator() —— 可输出 "订单计算器已创建" |
| 方法 | double calculateTotal() —— 遍历商品,计算 单价 × 数量 的总和 |
| 方法 | double calculateDiscountedTotal(double rate) —— 调用 calculateTotal() 后乘以折扣率 |
| 方法 | void printReceipt() —— 逐行打印每个商品的名称、单价、数量、小计,最后打印总价和折后价 |
TestShoppingCart 类:
准备测试数据:
String[] items = {"笔记本", "钢笔", "橡皮"};
double[] prices = {15.5, 5.0, 2.0};
int[] quantities = {2, 3, 5};
创建 ShoppingCart 对象,调用 showCart() 显示购物车
调用 checkout(0.8) 传入 8 折折扣,观察输出
验证局部内部类的封装性:尝试在 main 方法中直接创建 OrderCalculator 对象:
// 这行应编译报错 —— OrderCalculator 是局部内部类,仅在 checkout 方法内可见
// OrderCalculator calculator = new OrderCalculator(); // ❌
局部内部类的作用域是什么?它可以在哪些位置被访问?
在 OrderCalculator 中访问了 checkout 方法的参数 discountRate。从 Java 8 开始,这个参数有什么要求?(提示:effectively final)
如果将 OrderCalculator 改成成员内部类,设计上是否合理?为什么?
局部内部类除了可以访问外部类的成员,还能访问什么?
========== 购物车信息 ==========
商品清单:
笔记本 ¥15.5 × 2 = ¥31.0
钢笔 ¥5.0 × 3 = ¥15.0
橡皮 ¥2.0 × 5 = ¥10.0
========== 开始结算(折扣率:0.8)==========
订单计算器已创建
---------- 购物清单 ----------
笔记本 ¥15.5 × 2 = ¥31.0
钢笔 ¥5.0 × 3 = ¥15.0
橡皮 ¥2.0 × 5 = ¥10.0
-------------------------------
商品总价:¥56.0
折扣后价:¥44.8
知识点:私有成员内部类、静态成员内部类、局部内部类综合运用、内部类的设计原则
设计一个 学校管理系统,要求使用不同类型的内部类来组织代码:
holdClass() 方法中使用,应设计为 局部内部类| 成员 | 说明 |
|---|---|
| 私有属性 | schoolName(学校名称,String) |
| 私有属性 | address(地址,String) |
| 构造方法 | School(String schoolName, String address) |
| 方法 | void showInfo() —— 输出学校信息 |
| 方法 | Student createStudent(String name, int age, String studentId) —— 创建并返回学生对象(因为 Student 是私有的,外界无法直接 new) |
| 方法 | void holdClass(TeacherInfo teacher, String course) —— 上课方法,内部使用 局部内部类 记录出勤 |
| 方法 | static void showSchoolRule() —— 输出学校校训(静态方法) |
| 成员 | 说明 |
|---|---|
| 私有属性 | name(姓名,String)、age(年龄,int)、studentId(学号,String) |
| 构造方法 | Student(String name, int age, String studentId) |
| 方法 | void study() —— 输出 "学生 xxx(学号:xxx)正在学习..." |
| 方法 | void showInfo() —— 输出学生完整信息 |
| 成员 | 说明 |
|---|---|
| 私有属性 | name(姓名,String)、subject(教授科目,String)、salary(工资,double) |
| 构造方法 | TeacherInfo(String name, String subject, double salary) |
| 方法 | void teach() —— 输出 "教师 xxx 正在教授 xxx..." |
| 方法 | void showInfo() —— 输出教师信息(含学校名称——思考能否直接访问?) |
| 静态方法 | static void showTeacherRule() —— 输出 "为人师表,教书育人" |
AttendanceRecorder| 成员 | 说明 |
|---|---|
| 私有属性 | attendedStudents(出勤学生名单,String[]),通过构造方法传入 |
| 构造方法 | AttendanceRecorder(String[] attendedStudents) |
| 方法 | void record() —— 遍历出勤名单,逐行输出 "✅ xxx 已签到" |
| 方法 | void showSummary() —— 输出出勤总人数 |
TestSchool 类:
创建 School 对象("清华附中", "北京市海淀区")
通过 School 创建学生——Student 是私有内部类,不能直接 new:
// Student s = new Student("张三", 18, "2024001"); // ❌ 编译报错——Student 是私有的
// ❌ 也无法通过 School.Student 方式创建,因为 private 限制
// ✅ 必须通过 School 的公共方法创建
// 假设 School 中有 createStudent 方法
直接创建教师对象——TeacherInfo 是静态内部类,且非私有:
School.TeacherInfo teacher = new School.TeacherInfo("李老师", "数学", 8000.0);
teacher.teach();
teacher.showInfo();
调用 holdClass() 方法上课,传入教师对象和课程名称
调用 School.showSchoolRule() 和 School.TeacherInfo.showTeacherRule() 静态方法
在本系统中,Student 被设计为 private 内部类,TeacherInfo 没有使用 private。根据内部类的设计原则,这样设计是否合理?从"外部类对内部类的控制权"角度分析。
在 TeacherInfo.showInfo() 中,如果想要输出学校名称 schoolName,能否直接访问?如果不能,应该如何改造代码?(提示:静态内部类不能直接访问外部类的实例成员)
AttendanceRecorder 作为局部内部类,它的作用范围是什么?如果在 holdClass 方法外部创建一个 AttendanceRecorder 对象,会发生什么?
对比练习 1 和练习 3 中的静态内部类,总结静态内部类在什么场景下更适用。
========== 学校信息 ==========
清华附中(地址:北京市海淀区)
========== 创建学生 ==========
学生 张三(学号:2024001)正在学习...
学生 李四(学号:2024002)正在学习...
========== 教师信息 ==========
教师 李老师 正在教授 数学...
教师信息:李老师 | 数学 | 工资:8000.0
—— 所属学校:清华附中
========== 开始上课 ==========
教师 李老师 正在教授 数学...
✅ 张三 已签到
✅ 李四 已签到
✅ 王五 已签到
今日出勤人数:3 人
========== 校规校训 ==========
学校校训:自强不息,厚德载物
教师守则:为人师表,教书育人
| 内部类类型 | 修饰符 | 创建格式 | 能否直接访问外部类实例成员 | 适用场景 |
|---|---|---|---|---|
| 成员内部类 | 无 / private / public | 外部类.new 内部类() |
✅ 可以 | 内部细节需依赖外部类实例(如心脏之于人) |
| 私有成员内部类 | private |
通过外部类方法返回 | ✅ 可以 | 不想让外界直接访问,完全由外部类控制 |
| 静态成员内部类 | static |
new 外部类.内部类() |
❌ 不可以(只能访问静态成员) | 内部类不依赖外部类实例,可独立使用 |
| 局部内部类 | 非修饰符(方法内定义) | 仅在方法内 new |
✅ 可以(且可访问方法局部变量) | 仅在某方法内使用的临时逻辑 |