20260727-内部类练习.md 13 KB

2026-07-27 内部类(Inner Class)进阶练习(课堂练习用)

说明:今日习题围绕 内部类(Inner Class) 深入展开,重点覆盖 静态成员内部类局部内部类,并综合巩固成员内部类、私有内部类的设计原则。共 3 道练习,难度递进。


练习 1:静态成员内部类——餐厅菜单与厨师信息 ⭐⭐

知识点:静态成员内部类、static 修饰内部类的特点、静态内部类的创建格式、静态内部类与外部类的关系

需求说明

餐厅(Restaurant)有自己的 菜单项(MenuItem)厨师(Chef)。菜单项和厨师信息可以脱离具体的餐厅实例独立存在(例如连锁餐厅采用统一标准),但它们在业务逻辑上又属于餐厅的一部分。请使用 静态成员内部类(static 修饰) 来设计这个体系。

静态内部类使用 static 修饰,创建时不需要先创建外部类对象,格式为 new 外部类名.内部类名(...)

类设计

Restaurant 类(外部类)

成员 说明
私有属性 name(店名,String)、address(地址,String)
构造方法 Restaurant(String name, String address)
方法 void showInfo() —— 输出 "xxx(地址:xxx)"
方法 void openRestaurant() —— 输出餐厅开业信息,依次调用每个厨师和菜单项的方法

MenuItem 类(静态成员内部类

成员 说明
私有属性 dishName(菜品名,String)、price(价格,double)
构造方法 MenuItem(String dishName, double price)
方法 void showDish() —— 输出 "xxx —— ¥xxx"
方法 void describe() —— 输出 "这是 xxx,价格 ¥xxx,欢迎品尝"

Chef 类(静态成员内部类

成员 说明
私有属性 name(姓名,String)、specialty(拿手菜,String)
构造方法 Chef(String name, String specialty)
方法 void cook() —— 输出 "厨师 xxx 正在烹饪拿手菜:xxx"
方法 void showInfo() —— 输出 "厨师:xxx \| 拿手菜:xxx"

测试类要求

TestRestaurant

  1. 直接创建菜单项对象(不依赖 Restaurant 实例):

    Restaurant.MenuItem item1 = new Restaurant.MenuItem("宫保鸡丁", 38.0);
    Restaurant.MenuItem item2 = new Restaurant.MenuItem("麻婆豆腐", 28.0);
    

    静态内部类的创建格式:外部类名.内部类名 对象名 = new 外部类名.内部类名(...)

  2. 直接创建厨师对象(不依赖 Restaurant 实例):

    Restaurant.Chef chef1 = new Restaurant.Chef("王师傅", "宫保鸡丁");
    Restaurant.Chef chef2 = new Restaurant.Chef("李师傅", "麻婆豆腐");
    
  3. 分别调用菜单项和厨师的方法展示信息

  4. 创建 Restaurant 对象("川味轩", "北京市海淀区"),调用 openRestaurant()

  5. 尝试:在 Chefcook() 方法中直接访问外部类的 name 属性——观察是否编译报错,理解静态内部类的访问限制

思考题

  1. 静态内部类和普通成员内部类在 创建方式 上有什么区别?从 new 的语法角度具体说明。

  2. 静态内部类能否直接访问外部类的 实例成员(如 nameaddress)?为什么?

  3. 如果确实需要在静态内部类中访问外部类的实例成员,应该如何改造代码?(提示:通过参数将外部类引用传入静态内部类)

  4. 在这个场景中,MenuItemChef 设计为静态内部类是否合理?如果改为普通成员内部类,会对创建方式产生什么影响?

预期输出

========== 菜单信息 ==========
宫保鸡丁 —— ¥38.0
这是 宫保鸡丁,价格 ¥38.0,欢迎品尝
麻婆豆腐 —— ¥28.0
这是 麻婆豆腐,价格 ¥28.0,欢迎品尝

========== 厨师信息 ==========
厨师:王师傅 | 拿手菜:宫保鸡丁
厨师:李师傅 | 拿手菜:麻婆豆腐

========== 餐厅开业 ==========
川味轩(地址:北京市海淀区)开业啦!
厨师 王师傅 正在烹饪拿手菜:宫保鸡丁
厨师 李师傅 正在烹饪拿手菜:麻婆豆腐
欢迎莅临品尝!

练习 2:局部内部类——网上购物车结算系统 ⭐⭐

知识点:局部内部类、局部内部类的作用域、局部内部类访问方法参数和局部变量、effectively final

需求说明

在电商购物车结算时,我们需要计算商品的总价、折扣价等。计算逻辑只在该次结算方法中使用,不需要对外暴露,因此非常适合设计为 局部内部类

类设计

ShoppingCart 类(外部类)

成员 说明
私有属性 items(商品名称数组,String[]
私有属性 prices(单价数组,double[]
私有属性 quantities(数量数组,int[]
构造方法 ShoppingCart(String[] items, double[] prices, int[] quantities) —— 三个数组长度应一致
方法 void checkout(double discountRate) —— 结算方法,内部定义局部内部类 完成计算
方法 void showCart() —— 显示购物车中的所有商品信息

checkout 方法中的局部内部类 OrderCalculator

checkout(double discountRate) 方法内部定义以下局部内部类:

成员 说明
构造方法 OrderCalculator() —— 可输出 "订单计算器已创建"
方法 double calculateTotal() —— 遍历商品,计算 单价 × 数量 的总和
方法 double calculateDiscountedTotal(double rate) —— 调用 calculateTotal() 后乘以折扣率
方法 void printReceipt() —— 逐行打印每个商品的名称、单价、数量、小计,最后打印总价和折后价

测试类要求

TestShoppingCart

  1. 准备测试数据:

    String[] items = {"笔记本", "钢笔",  "橡皮"};
    double[] prices = {15.5,    5.0,    2.0};
    int[] quantities = {2,       3,      5};
    
  2. 创建 ShoppingCart 对象,调用 showCart() 显示购物车

  3. 调用 checkout(0.8) 传入 8 折折扣,观察输出

  4. 验证局部内部类的封装性:尝试在 main 方法中直接创建 OrderCalculator 对象:

    // 这行应编译报错 —— OrderCalculator 是局部内部类,仅在 checkout 方法内可见
    // OrderCalculator calculator = new OrderCalculator();  // ❌
    

思考题

  1. 局部内部类的作用域是什么?它可以在哪些位置被访问?

  2. OrderCalculator 中访问了 checkout 方法的参数 discountRate。从 Java 8 开始,这个参数有什么要求?(提示:effectively final)

  3. 如果将 OrderCalculator 改成成员内部类,设计上是否合理?为什么?

  4. 局部内部类除了可以访问外部类的成员,还能访问什么?

预期输出

========== 购物车信息 ==========
商品清单:
  笔记本   ¥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

练习 3:综合练习——学校管理系统(成员内部类 + 静态内部类 + 局部内部类)⭐⭐⭐

知识点:私有成员内部类、静态成员内部类、局部内部类综合运用、内部类的设计原则

需求说明

设计一个 学校管理系统,要求使用不同类型的内部类来组织代码:

  • 学生(Student) 是学校的核心成员,应设计为 私有成员内部类,只能通过学校对象创建
  • 教师信息(TeacherInfo) 是学校的公共信息,应设计为 静态成员内部类,可独立访问
  • 上课点名(AttendanceRecorder) 仅在 holdClass() 方法中使用,应设计为 局部内部类

类设计

School 类(外部类)

成员 说明
私有属性 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() —— 输出学校校训(静态方法)

Student 类(私有成员内部类

成员 说明
私有属性 name(姓名,String)、age(年龄,int)、studentId(学号,String)
构造方法 Student(String name, int age, String studentId)
方法 void study() —— 输出 "学生 xxx(学号:xxx)正在学习..."
方法 void showInfo() —— 输出学生完整信息

TeacherInfo 类(静态成员内部类

成员 说明
私有属性 name(姓名,String)、subject(教授科目,String)、salary(工资,double)
构造方法 TeacherInfo(String name, String subject, double salary)
方法 void teach() —— 输出 "教师 xxx 正在教授 xxx..."
方法 void showInfo() —— 输出教师信息(含学校名称——思考能否直接访问?)
静态方法 static void showTeacherRule() —— 输出 "为人师表,教书育人"

holdClass 方法中的局部内部类 AttendanceRecorder

成员 说明
私有属性 attendedStudents(出勤学生名单,String[]),通过构造方法传入
构造方法 AttendanceRecorder(String[] attendedStudents)
方法 void record() —— 遍历出勤名单,逐行输出 "✅ xxx 已签到"
方法 void showSummary() —— 输出出勤总人数

测试类要求

TestSchool

  1. 创建 School 对象("清华附中", "北京市海淀区")

  2. 通过 School 创建学生——Student 是私有内部类,不能直接 new

    // Student s = new Student("张三", 18, "2024001");    // ❌ 编译报错——Student 是私有的
    // ❌ 也无法通过 School.Student 方式创建,因为 private 限制
    
    // ✅ 必须通过 School 的公共方法创建
    // 假设 School 中有 createStudent 方法
    
  3. 直接创建教师对象——TeacherInfo 是静态内部类,且非私有:

    School.TeacherInfo teacher = new School.TeacherInfo("李老师", "数学", 8000.0);
    teacher.teach();
    teacher.showInfo();
    
  4. 调用 holdClass() 方法上课,传入教师对象和课程名称

  5. 调用 School.showSchoolRule()School.TeacherInfo.showTeacherRule() 静态方法

思考题

  1. 在本系统中,Student 被设计为 private 内部类,TeacherInfo 没有使用 private。根据内部类的设计原则,这样设计是否合理?从"外部类对内部类的控制权"角度分析。

  2. TeacherInfo.showInfo() 中,如果想要输出学校名称 schoolName,能否直接访问?如果不能,应该如何改造代码?(提示:静态内部类不能直接访问外部类的实例成员)

  3. AttendanceRecorder 作为局部内部类,它的作用范围是什么?如果在 holdClass 方法外部创建一个 AttendanceRecorder 对象,会发生什么?

  4. 对比练习 1 和练习 3 中的静态内部类,总结静态内部类在什么场景下更适用。

预期输出

========== 学校信息 ==========
清华附中(地址:北京市海淀区)

========== 创建学生 ==========
学生 张三(学号:2024001)正在学习...
学生 李四(学号:2024002)正在学习...

========== 教师信息 ==========
教师 李老师 正在教授 数学...
教师信息:李老师 | 数学 | 工资:8000.0
—— 所属学校:清华附中

========== 开始上课 ==========
教师 李老师 正在教授 数学...
✅ 张三 已签到
✅ 李四 已签到
✅ 王五 已签到
今日出勤人数:3 人

========== 校规校训 ==========
学校校训:自强不息,厚德载物
教师守则:为人师表,教书育人

附:内部类类型对比总结

内部类类型 修饰符 创建格式 能否直接访问外部类实例成员 适用场景
成员内部类 无 / private / public 外部类.new 内部类() ✅ 可以 内部细节需依赖外部类实例(如心脏之于人)
私有成员内部类 private 通过外部类方法返回 ✅ 可以 不想让外界直接访问,完全由外部类控制
静态成员内部类 static new 外部类.内部类() ❌ 不可以(只能访问静态成员) 内部类不依赖外部类实例,可独立使用
局部内部类 非修饰符(方法内定义) 仅在方法内 new ✅ 可以(且可访问方法局部变量) 仅在某方法内使用的临时逻辑