# 2026-07-27 内部类(Inner Class)基础练习(课堂练习用) > **说明**:以下习题围绕今日授课核心内容——**内部类(Inner Class)** 展开,涵盖成员内部类、私有内部类的设计原则、静态成员内部类、局部内部类等知识点。 > > 题目分为两部分:**随堂练习**(内部类基础,4 题)和 **进阶练习**(综合挑战,2 题)。 --- # 第一部分:随堂练习 > 以下练习围绕内部类的基础概念和用法,由浅入深。 --- ## 练习 1:内部类入门——汽车与发动机 **难度**:⭐ **知识点**:内部类概念、成员内部类定义、内部类访问外部类成员 **场景描述**:汽车(`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` 要求**: ```java 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 安静了... ``` **思考题**: 1. 从上述代码看,内部类访问外部类的私有成员时,是否需要 getter 方法? 2. 如果 `Engine` 改为私有内部类,外界(如 `CarTest`)能否直接创建 `Engine` 对象? 3. 本练习中 `Engine` 对象的创建是在 `Car` 的内部方法 `drive()` 中完成的,这符合「外部类访问内部类必须创建对象」的特点吗? --- ## 练习 2:私有内部类设计——人(Person)与心脏(Heart) **难度**:⭐⭐ **知识点**:私有内部类、外部类提供公共方法暴露内部类功能、设计原则 **场景描述**:人有心脏,心脏离开人体无法独立存在。按照 **私有内部类设计原则**,心脏应该作为人的私有内部类,外界必须通过人的公共方法才能使用心脏的功能。 **`Person` 类**(外部类): - **私有属性**:`name`(姓名,String) - **构造方法**:`Person(String name)` - **私有内部类** `Heart`: - **普通方法**:`void beat()` —— 输出 `"{name} 的心脏在跳动:咚咚咚..."`(直接访问外部类私有属性) - **普通方法**:`void rest()` —— 输出 `"{name} 的心脏在休息:咚...咚...咚..."` - **公共方法**:`void heartbeat()` —— 在方法内部创建 `Heart` 对象并调用 `beat()` 和 `rest()` **测试类 `PersonTest` 要求**: ```java 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(); // 编译能通过吗?为什么? } } ``` **预期输出**: ``` 张三 的心脏在跳动:咚咚咚... 张三 的心脏在休息:咚...咚...咚... ``` **请回答以下问题**: 1. 将 `Heart` 类设为 `private` 的好处是什么? 2. 如果 `Person` 类想对外提供「获取心脏对象」的能力,应该如何修改代码?(参考课堂笔记中 `getHeart()` 方法的设计) 3. 如果不使用私有内部类,而是将 `Heart` 写成一个独立类,让 `Person` 持有 `Heart` 成员变量,会有什么弊端?(提示:外界可能跳过 `Person` 直接创建 `Heart` 对象) **代码补充挑战**:请将 `Person` 类补充完整: ```java public class Person { private String name; // 构造方法 public Person(String name) { this.name = name; } // 私有内部类 Heart // 请补充代码 // 公共方法 heartbeat() // 请补充代码 } ``` --- ## 练习 3:静态成员内部类——学校与学生证打印 **难度**:⭐⭐ **知识点**:静态成员内部类、静态内部类创建格式、静态内部类与外部类的关系 **场景描述**:每所学校(`School`)都有统一格式的学生证(`StudentCard`),其中包含学校名称和学生的个人信息。学生证的模板格式由学校统一规定(静态内部类),不依赖某个具体的学校对象。 **`School` 类**(外部类): - **私有静态属性**:`schoolName`(学校名称,static String)—— 所有学校对象共享 - **静态代码块**:初始化 `schoolName` 为 `"四福科技大学"` - **静态成员内部类** `StudentCard`:用 `static` 修饰 - **私有属性**:`studentName`(学生姓名,String)、`studentId`(学号,String) - **构造方法**:`StudentCard(String studentName, String studentId)` - **普通方法**:`void showCard()` —— 输出学生证信息(学校名称使用外部类的静态属性) **测试类 `SchoolTest` 要求**: ```java 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 ``` **思考题**: 1. 静态内部类和普通成员内部类在创建对象时的格式有什么区别? 2. 静态内部类能否访问外部类的 **非静态** 成员(如实例变量)?为什么? 3. 如果 `schoolName` 不是 `static` 的,上面的代码能否正常运行?应该如何修改? --- ## 练习 4:局部内部类——餐厅点餐系统 **难度**:⭐⭐⭐ **知识点**:局部内部类、定义在方法内部的类、局部内部类的作用域 **场景描述**:餐厅(`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` 要求**: ```java 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 元,等待配送... ``` **思考题**: 1. 局部内部类的作用范围是什么?方法外部能否访问? 2. 局部内部类除了可以访问外部类的成员,还可以访问所在方法内的 **局部变量**。请在 `dineIn()` 方法中添加一个局部变量 `String welcome = "欢迎光临";`,尝试在 `DineInOrder` 内部类中访问它,观察是否能编译通过。 3. 局部内部类和成员内部类在使用场景上有什么本质区别? --- # 第二部分:进阶练习 > 以下为综合挑战题,融合多种内部类类型、内部类访问规则、设计原则等知识点。 --- ## 第 1 题:综合应用——计算机与外设(成员内部类 + 静态内部类)⭐⭐⭐⭐ ### 需求说明 模拟计算机(`Computer`)的组成结构。计算机包含: - **CPU** —— 计算机的核心处理部件,每台计算机有自己的 CPU,CPU 的行为与具体的计算机相关(**成员内部类**) - **USB 接口规范** —— 所有计算机的 USB 接口规范是统一的,不依赖具体的计算机实例(**静态内部类**) - **内存条** —— 计算机的内存,可更换升级(**普通成员内部类**,但设计为私有) ### 类设计 **`Computer` 类**(外部类): **私有属性**: | 属性 | 类型 | 说明 | |------|------|------| | `brand` | String | 品牌 | | `model` | String | 型号 | | `ram` | Memory | 内存条对象(内部类) | **构造方法**:`Computer(String brand, String model, int ramSize)` —— 初始化品牌、型号,并创建 `Memory` 对象(内存大小为 `ramSize` GB) #### 内部类 1:CPU(成员内部类,非私有) **`CPU` 类**(成员内部类): - **私有属性**:`cores`(核心数,int)、`frequency`(主频,double,单位 GHz) - **构造方法**:`CPU(int cores, double frequency)` - **普通方法**:`void process()` —— 输出 `"{brand} {model} CPU 正在计算:{cores} 核 {frequency}GHz 全速运行..."`(访问外部类私有属性) #### 内部类 2:UsbSpec(静态成员内部类) **`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} 协议传输数据"` #### 内部类 3:Memory(私有成员内部类) **`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` 类**: ```java 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 全速运行... ``` ### 思考题 1. 本练习中为什么 `UsbSpec` 设计为 **静态内部类**?USB 接口规范和具体的计算机实例有关吗? 2. 为什么 `Memory` 设计为 **私有内部类**?外界可以直接更换内存吗?(提示:通过 `replaceMemory()` 方法) 3. 成员内部类 `CPU` 没有被 `private` 修饰,外界可以直接创建 `Computer.CPU` 对象吗?如果可以,创建格式是什么? 4. 如果将 `UsbSpec` 改为普通成员内部类(去掉 `static`),创建 `UsbSpec` 对象的格式会变成什么样? --- ## 第 2 题:综合应用——银行账户系统(私有内部类 + 局部内部类 + 静态内部类)⭐⭐⭐⭐⭐ ### 需求说明 模拟银行账户系统。一个银行(`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` 类**: ```java 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` 类型。 ### 预期输出(方案 A 参考) ``` ========== 四福银行 利率信息 ========== 储蓄年利率:2.5% 贷款年利率:4.5% 1 万元存 3 年,利息:768.91 元 ========== 开户与交易 ========== 开户成功!欢迎 张三 成为 四福银行 的客户 【存款交易】开始... 张三 存入 5000.0 元,余额:5000.0 元(银行:四福银行) 【存款交易】开始... 张三 存入 2000.0 元,余额:7000.0 元(银行:四福银行) 【取款交易】开始... 张三 取出 1000.0 元,余额:6000.0 元 取款失败:张三 余额不足(当前余额:6000.0 元) ``` ### 思考题 1. 为什么 `Account` 设计为 `Bank` 的私有内部类?这体现了什么设计原则?(参考课堂笔记中 `Heart` 私有化的原因) 2. 局部内部类 `DepositTransaction` 和 `WithdrawTransaction` 为什么定义在方法内部,而不是定义为成员内部类? 3. 静态内部类 `RateInfo` 中的 `showRate()` 是静态方法,不需要创建对象就能调用;而 `calculateInterest()` 是普通方法,需要创建对象才能调用。这两种方式分别适用于什么场景? 4. 如果去掉 `RateInfo` 的 `static` 修饰,上面的代码中 `Bank.RateInfo.showRate()` 还能编译通过吗?`new Bank.RateInfo()` 还能编译通过吗? --- ## 📋 题目总览 | 序号 | 所属部分 | 核心知识点 | 难度 | |------|---------|-----------|------| | 练习 1 | 随堂 | 成员内部类概念、内部类访问外部类成员 | ⭐ | | 练习 2 | 随堂 | 私有内部类设计原则、外部类提供公共方法暴露内部类功能 | ⭐⭐ | | 练习 3 | 随堂 | 静态成员内部类、创建格式、与外部类的关系 | ⭐⭐ | | 练习 4 | 随堂 | 局部内部类、定义在方法内部、作用域限制 | ⭐⭐⭐ | | 进阶 1 | 进阶 | 成员内部类 + 静态内部类 + 私有内部类综合运用 | ⭐⭐⭐⭐ | | 进阶 2 | 进阶 | 私有内部类 + 局部内部类 + 静态内部类 + 设计模式综合 | ⭐⭐⭐⭐⭐ |