Java 内部类学习笔记
概述
内部类是指定义在另一个类内部的类,Java提供了三种主要的内部类形式,每种都有不同的特性和使用场景。
1. Inner Class(成员内部类)
特点
- 定义在外部类的内部,与成员变量和方法同级
- 可以访问外部类的所有成员(包括private)
- 不能包含静态成员(static final常量除外)
- 必须先创建外部类实例才能创建内部类实例
语法
class OuterClass {
private String outerField = "外部字段";
class InnerClass {
void display() {
System.out.println("访问外部字段: " + outerField);
}
}
}
示例
// 外部类
class University {
private String universityName = "清华大学";
// 内部类
class Department {
private String deptName;
public Department(String name) {
this.deptName = name;
}
void showInfo() {
// 直接访问外部类的私有字段
System.out.println("大学: " + universityName);
System.out.println("院系: " + deptName);
}
// 可以访问外部类的this引用
void showOuterThis() {
System.out.println("内部类this: " + this);
System.out.println("外部类this: " + University.this);
}
}
}
// 使用示例
public class InnerClassExample {
public static void main(String[] args) {
University tsinghua = new University();
// 创建内部类实例
University.Department csDept = tsinghua.new Department("计算机科学");
University.Department mathDept = tsinghua.new Department("数学系");
csDept.showInfo();
mathDept.showInfo();
}
}
2. Static Nested Class(静态嵌套类)
特点
- 使用static关键字修饰的内部类
- 不能直接访问外部类的非静态成员
- 可以包含静态成员
- 不需要外部类实例即可创建
- 行为上更像一个独立的类
语法
class OuterClass {
private static String staticField = "静态字段";
private String instanceField = "实例字段";
static class StaticNestedClass {
void display() {
System.out.println("只能访问静态字段: " + staticField);
// System.out.println(instanceField); // 编译错误!
}
}
}
示例
// 外部类
class Calculator {
private static String brand = "科学计算器";
private double batteryLevel = 100.0;
// 静态嵌套类
static class Operation {
private String operationName;
private static int operationCount = 0;
public Operation(String name) {
this.operationName = name;
operationCount++;
}
public double calculate(double a, double b) {
System.out.println("使用" + brand + "进行计算"); // 只能访问静态字段
System.out.println("操作类型: " + operationName);
switch(operationName) {
case "加法": return a + b;
case "减法": return a - b;
case "乘法": return a * b;
case "除法": return a / b;
default: return 0;
}
}
public static int getOperationCount() {
return operationCount;
}
}
}
// 使用示例
public class StaticNestedClassExample {
public static void main(String[] args) {
// 不需要外部类实例,直接创建静态嵌套类
Calculator.Operation add = new Calculator.Operation("加法");
Calculator.Operation multiply = new Calculator.Operation("乘法");
System.out.println("5 + 3 = " + add.calculate(5, 3));
System.out.println("5 × 3 = " + multiply.calculate(5, 3));
System.out.println("操作总数: " + Calculator.Operation.getOperationCount());
}
}
3. Anonymous Class(匿名内部类)
特点
- 没有类名的内部类
- 通常用于实现接口或继承类的一次性使用
- 简洁但功能有限
- 常用于事件监听器、线程等场景
语法
// 创建一个继承自某个类的匿名类
父类 对象名 = new 父类() {
// 重写父类中的方法
};
// 创建一个实现某个接口的匿名类
接口 对象名 = new 接口() {
// 实现接口中的方法
};
interface MyInterface {
void method();
}
class OuterClass {
void createAnonymous() {
MyInterface obj = new MyInterface() {
@Override
public void method() {
System.out.println("匿名类实现");
}
};
}
}
示例
// 接口定义
interface ButtonClickListener {
void onClick();
void onDoubleClick();
}
interface Runnable {
void run();
}
// 外部类
class GUIApplication {
private String appName;
public GUIApplication(String name) {
this.appName = name;
}
public void createButton() {
// 匿名内部类实现接口
ButtonClickListener listener = new ButtonClickListener() {
private int clickCount = 0;
@Override
public void onClick() {
clickCount++;
System.out.println(appName + " - 按钮被点击! 次数: " + clickCount);
}
@Override
public void onDoubleClick() {
System.out.println(appName + " - 按钮被双击!");
}
};
// 模拟按钮点击
listener.onClick();
listener.onDoubleClick();
}
public void startThread() {
// 匿名内部类继承Thread类
Thread workerThread = new Thread() {
@Override
public void run() {
for(int i = 1; i <= 3; i++) {
System.out.println(appName + " - 线程执行中: " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
workerThread.start();
}
public void createRunnable() {
// 使用Runnable接口的匿名内部类
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println(appName + " - Runnable任务执行");
}
};
task.run();
}
}
// 使用示例
public class AnonymousClassExample {
public static void main(String[] args) {
GUIApplication app = new GUIApplication("我的应用");
app.createButton();
app.startThread();
app.createRunnable();
try {
Thread.sleep(4000); // 等待线程执行完成
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
三种内部类的比较
特性 | Inner Class | Static Nested Class | Anonymous Class |
---|---|---|---|
访问外部类成员 | 可以访问所有成员 | 只能访问静态成员 | 可以访问final或等效final的局部变量 |
静态成员 | 不能有(除常量) | 可以有 | 不能有 |
创建方式 | 需要外部类实例 | 直接创建 | 在声明时创建 |
命名 | 有明确类名 | 有明确类名 | 无类名 |
使用场景 | 紧密关联的组件 | 工具类、辅助类 | 一次性实现、回调 |
实际应用场景
1. 迭代器模式(Inner Class)
class BookShelf {
private String[] books;
private int count;
public BookShelf(int capacity) {
books = new String[capacity];
count = 0;
}
public void addBook(String book) {
if(count < books.length) {
books[count++] = book;
}
}
// 内部类实现迭代器
public class BookIterator {
private int index = 0;
public boolean hasNext() {
return index < count;
}
public String next() {
return books[index++];
}
}
public BookIterator iterator() {
return new BookIterator();
}
}
2. Builder模式(Static Nested Class)
class Computer {
private String cpu;
private String memory;
private String storage;
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.memory = builder.memory;
this.storage = builder.storage;
}
// 静态嵌套类实现Builder模式
public static class Builder {
private String cpu;
private String memory;
private String storage;
public Builder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder setMemory(String memory) {
this.memory = memory;
return this;
}
public Builder setStorage(String storage) {
this.storage = storage;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
注意事项
- 内存泄漏:内部类持有外部类引用,可能造成内存泄漏
- 序列化:内部类的序列化可能有问题
- 测试难度:内部类可能增加单元测试的复杂度
- 可读性:过度使用内部类可能降低代码可读性
总结
- Inner Class:适用于需要紧密访问外部类状态的场景
- Static Nested Class:适用于与外部类相关但不依赖其实例的场景
- Anonymous Class:适用于简单的一次性实现场景
选择合适的内部类类型可以使代码更加清晰、模块化,并提高封装性。