# 2026-07-27 内部类(Inner Class)进阶练习(课堂练习用) > **说明**:今日习题围绕 **内部类(Inner Class)** 深入展开,重点覆盖 **静态成员内部类**、**局部内部类**,并综合巩固成员内部类、私有内部类的设计原则。共 **3 道练习**,难度递进。 --- ## 练习 1:静态成员内部类——电脑硬件升级(显卡与硬盘)⭐⭐ **知识点**:静态成员内部类、`static` 修饰内部类的特点、静态内部类的创建格式、静态内部类与外部类的关系 ### 需求说明 在之前的练习中,我们使用 **普通成员内部类** 设计了计算机的 CPU 和内存。这次,我们换成 **静态成员内部类** 来设计电脑的 **显卡(GraphicsCard)** 和 **硬盘(HardDisk)**,体验静态内部类在创建方式和访问权限上的不同。 > 显卡和硬盘同样是计算机的重要部件,但使用 `static` 修饰后,它们可以不依赖外部类实例而独立创建。 ### 类设计 #### Computer 类(外部类) | 成员 | 说明 | |------|------| | 私有属性 | `brand`(品牌,String)、`price`(价格,double) | | 构造方法 | `Computer(String brand, double price)` | | 方法 | `void showInfo()` —— 输出电脑品牌和价格 | | 方法 | `void startComputer()` —— 输出启动信息,调用显卡的 `render()` 和硬盘的 `readData()` | #### GraphicsCard 类(**静态成员内部类**) | 成员 | 说明 | |------|------| | 私有属性 | `model`(型号,String)、`vram`(显存,int,单位 GB) | | 构造方法 | `GraphicsCard(String model, int vram)` | | 方法 | `void render()` —— 输出 `"显卡(xxx,xxx GB)正在渲染画面..."` | | 方法 | `void showSpec()` —— 输出 `"显卡规格:xxx,显存:xxx GB"` | #### HardDisk 类(**静态成员内部类**) | 成员 | 说明 | |------|------| | 私有属性 | `capacity`(容量,int,单位 GB)、`type`(类型,String,如 "SSD" / "HDD") | | 构造方法 | `HardDisk(int capacity, String type)` | | 方法 | `void readData()` —— 输出 `"硬盘(xxx GB,xxx)正在读取数据..."` | | 方法 | `void writeData()` —— 输出 `"硬盘(xxx GB,xxx)正在写入数据..."` | ### 测试类要求 **`TestComputer2` 类**: 1. **直接创建显卡对象**: ```java Computer.GraphicsCard gpu = new Computer.GraphicsCard("RTX 4070", 12); ``` > 静态内部类 **不需要** 先创建外部类对象,格式为 `外部类名.内部类名` 2. **直接创建硬盘对象**: ```java Computer.HardDisk disk = new Computer.HardDisk(1024, "SSD"); ``` 3. 调用显卡的 `showSpec()` 和 `render()` 方法 4. 调用硬盘的 `readData()` 和 `writeData()` 方法 5. 创建 `Computer` 对象("华硕", 8999.0),调用 `startComputer()` 6. **尝试**:在 GraphicsCard 的 `render()` 方法中直接访问外部类的 `brand` 属性——观察是否编译报错 ### 思考题 1. 静态内部类和普通成员内部类在 **创建方式** 上有什么区别?从 `new` 的语法角度分析。 2. 静态内部类能否直接访问外部类的 **实例成员**(如 `brand`、`price`)?为什么? 3. 如果确实需要在静态内部类中访问外部类的实例成员,应该如何改造代码?(提示:通过参数传递外部类引用) 4. 对比之前的 CPU(成员内部类)和现在的 GraphicsCard(静态内部类),哪种内部类更适合描述"计算机的部件"这个场景?说说你的理由。 ### 预期输出 ``` ========== 独立创建显卡 ========== 显卡规格:RTX 4070,显存:12 GB 显卡(RTX 4070,12 GB)正在渲染画面... ========== 独立创建硬盘 ========== 硬盘(1024 GB,SSD)正在读取数据... 硬盘(1024 GB,SSD)正在写入数据... ========== 启动计算机 ========== 计算机 华硕 正在启动... 显卡(RTX 4070,12 GB)正在渲染画面... 硬盘(1024 GB,SSD)正在读取数据... ``` --- ## 练习 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. 准备测试数据: ```java 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` 对象: ```java // 这行应编译报错 —— 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`: ```java // Student s = new Student("张三", 18, "2024001"); // ❌ 编译报错——Student 是私有的 // ❌ 也无法通过 School.Student 方式创建,因为 private 限制 ``` ```java // ✅ 必须通过 School 的公共方法创建 // 假设 School 中有 createStudent 方法 ``` 3. **直接创建教师对象**——`TeacherInfo` 是静态内部类,且非私有: ```java 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` | ✅ 可以(且可访问方法局部变量) | 仅在某方法内使用的临时逻辑 |