编程自学指南:java程序设计开发,Java 内部类与枚举
(适用:Java 基础已掌握,首次接触内部类 / 枚举的学员)
一、学习目标
- 理解内部类的 4 种类型及适用场景
- 掌握枚举的定义与核心用法
- 能通过内部类 / 枚举优化代码结构
- 避免初学者常见误区(如内存泄漏、枚举滥用)
二、第一部分:内部类
1. 为什么需要内部类?(案例引入)
场景:设计一个 "手机" 类,包含 "电池" 类,电池仅属于手机内部使用
class Phone {
private String brand;
// 电池类作为内部成员
class Battery {
private int capacity;
void showInfo() {
System.out.println(brand + "电池容量:" + capacity); // 直接访问外部类私有属性
}
}
public static void main(String[] args) {
Phone phone = new Phone("小米");
Phone.Battery battery = phone.new Battery(); // 成员内部类创建方式
battery.capacity = 4500;
battery.showInfo(); // 输出:小米电池容量:4500
}
public Phone(String brand) { this.brand = brand; }
}
结论:内部类天然可以访问外部类私有成员,适合 "亲密类" 关系
2. 成员内部类(重点)
- 特点:依赖外部类实例,不能定义静态成员
- 内存关系:每个成员内部类对象隐含持有外部类引用(
外部类名.this
) - 案例:员工 - 工资条关系
class Employee {
private String name;
class SalarySlip { // 成员内部类
private double salary;
void print() {
System.out.println(name + "的工资:" + salary);
}
}
public void createSlip(double salary) {
SalarySlip slip = new SalarySlip(); // 直接创建
slip.salary = salary;
slip.print();
}
public Employee(String name) { this.name = name; }
}
// 使用:
new Employee("张三").createSlip(10000); // 输出:张三的工资:10000
3. 静态内部类(对比成员类)
- 特点:不依赖外部类实例,可定义静态成员
- 适用场景:工具类、辅助类(如 HashMap 的 Node 内部类)
- 案例:用户 - 地址管理(地址可独立存在)
class User {
private String username;
static class Address { // 静态内部类
private String city;
public Address(String city) { this.city = city; }
}
public static void main(String[] args) {
User.Address addr = new User.Address("北京"); // 无需User实例
}
}
4. 局部内部类(方法内的类)
- 特点:仅在方法内可见,可访问方法内的 final 变量(JDK8 + 隐含 final)
- 案例:抽奖算法(类仅在抽奖时使用)
class Lottery {
public void draw(String[] participants) {
class Winner { // 局部内部类
String name;
void announce() {
System.out.println("中奖者:" + name);
}
}
// 模拟中奖
int index = new Random().nextInt(participants.length);
Winner winner = new Winner();
winner.name = participants[index];
winner.announce();
}
}
5. 匿名内部类(重点难点)
- 本质:没有名字的局部内部类,直接继承 / 实现抽象类 / 接口
- 案例 1:按钮点击事件(GUI 编程常见)
import javax.swing.*;
import java.awt.event.*;
public class GUIExample {
public static void main(String[] args) {
JButton btn = new JButton("点击我");
// 匿名内部类实现ActionListener
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击!");
}
});
// ... 窗口显示代码
}
}
- 案例 2:简化线程创建
new Thread(() -> { // 匿名内部类(Lambda简化版)
System.out.println("新线程运行中");
}).start();
6. 内部类对比表(总结)
类型 | 是否依赖外部实例 | 作用域 | 常见用途 |
---|---|---|---|
成员内部类 | 是 | 外部类成员 | 亲密关联类 |
静态内部类 | 否 | 外部类静态范围 | 工具类、独立组件 |
局部内部类 | 是 | 方法 / 代码块内 | 临时功能封装 |
匿名内部类 | 是 | 表达式内 | 事件处理、回调函数 |
课堂练习
题目:设计一个Car
类,包含Engine
成员内部类(显示转速),Car
有start()
方法启动引擎。
三、第二部分:枚举
1. 从常量到枚举(痛点引入)
传统常量写法问题:
public static final int SEASON_SPRING = 1;
public static final int SEASON_SUMMER = 2;
// 问题1:类型不安全(可传入任意int)
// 问题2:无扩展性(新增季节需改代码)
枚举解决方案:
enum Season {
SPRING, SUMMER, AUTUMN, WINTER; // 常量实例
// 自定义方法
public String getDesc() {
switch(this) {
case SPRING: return "春暖花开";
// ... 其他季节
}
}
}
// 使用
Season s = Season.SPRING;
System.out.println(s.getDesc()); // 输出:春暖花开
2. 枚举基础语法
- 定义:
enum 枚举名 { 常量1, 常量2, ...; }
- 特性:
- 所有枚举类默认继承
java.lang.Enum
- 常量默认是
public static final
- 构造方法必须私有(隐式或显式)
- 所有枚举类默认继承
- 案例:星期枚举(含构造方法)
enum Weekday {
MONDAY("周一"), TUESDAY("周二"); // 调用构造方法
private String chineseName;
// 私有构造
Weekday(String chineseName) { this.chineseName = chineseName; }
public String getChineseName() { return chineseName; }
}
// 遍历所有常量
for(Weekday day : Weekday.values()) {
System.out.println(day + ":" + day.getChineseName());
}
3. 枚举高级用法
- 实现接口:
interface Behavior {
void action();
}
enum Animal implements Behavior {
DOG {
@Override public void action() { System.out.println("汪汪叫"); }
},
CAT {
@Override public void action() { System.out.println("喵喵叫"); }
};
}
// 使用
Animal.DOG.action(); // 输出:汪汪叫
- switch 增强(JDK12 + 模式匹配):
void handle(Season season) {
switch(season) {
case SPRING, AUTUMN -> System.out.println("舒适季节");
case SUMMER -> System.out.println("炎热");
case WINTER -> System.out.println("寒冷");
}
}
4. 枚举 vs 常量接口(面试高频)
对比项 | 枚举 | 常量接口(public static final) |
---|---|---|
类型安全 | 是(编译期检查) | 否(可传入任意 int) |
扩展性 | 好(新增常量不影响已有逻辑) | 差(需修改所有使用处的 switch) |
内存占用 | 小(全局唯一实例) | 无特殊优化 |
常用场景 | 状态机、有限选项集合 | 少量固定数值(如 HTTP 状态码) |
课堂练习
题目:设计订单状态枚举(WAIT_PAY, PAID, DELIVERED),每个状态包含中文描述和处理方法(如 PAID 状态可发货)。
四、初学者常见误区
-
内部类内存泄漏:成员内部类持有外部类引用,若外部类是 Activity(Android),可能导致内存泄漏
class Outer { private byte[] bigData = new byte[1024*1024]; // 大对象 class Inner { } void leak() { new Inner(); // 即使Outer不再使用,Inner仍持有Outer引用 } }
解决方案:静态内部类 + 弱引用,或及时置空外部类引用
-
枚举构造方法 public:枚举构造方法必须私有,否则编译报错
-
匿名内部类重写方法:必须实现抽象类 / 接口的所有抽象方法
-
枚举常量命名:必须全大写(规范),如
STATUS_SUCCESS
而非statusSuccess
五、课程总结
知识点 | 核心价值 | 典型案例 |
---|---|---|
成员内部类 | 封装亲密关系类,访问外部类私有成员 | 手机 - 电池、员工 - 工资条 |
匿名内部类 | 简化回调 / 事件处理代码 | 按钮点击、线程启动 |
枚举 | 类型安全的常量集合,替代 int 常量 | 季节、订单状态、星期 |
口诀:
内部类,类中类,成员局部静态匿;
枚举常量强类型,构造私有方法齐;
场景记牢别滥用,代码整洁又安全!
六、课后作业(必做 + 选做)
- 必做:用内部类实现一个
Person
类,包含Heart
成员内部类(模拟心跳),Person
有run()
方法时心跳加速。 - 必做:用枚举实现一个
Color
枚举,包含 RGB 值(如 RED (255,0,0))和获取十六进制字符串的方法。 - 选做:用匿名内部类优化 Java 集合的
Comparator
(如Collections.sort(list, new Comparator<>(){...})
)。
学习说明:所有案例可直接复制到 IDEA 运行