Java 内部类
内部类是定义在另一个类内部的类。Java 支持多种类型的内部类,每种类型有不同的用途和特性。理解内部类的使用是掌握 Java 高级特性的关键。本章将详细介绍 Java 中的内部类。
内部类的类型
Java 中有四种类型的内部类:
- 成员内部类(Member Inner Class)
- 静态内部类(Static Nested Class)
- 局部内部类(Local Inner Class)
- 匿名内部类(Anonymous Inner Class)
成员内部类
定义
成员内部类是定义在类中的非静态内部类。
public class Outer {
private String outerField = "外部类字段";
// 成员内部类
public class Inner {
private String innerField = "内部类字段";
public void display() {
System.out.println(outerField); // 可以访问外部类成员
System.out.println(innerField);
}
}
public void createInner() {
Inner inner = new Inner(); // 外部类中创建内部类
inner.display();
}
}
// 使用
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); // 通过外部类实例创建
inner.display();
特点
- 可以访问外部类所有成员:包括私有成员
- 不能 有静态成员:成员内部类不能有静态变量和静态方法(除非是常量)
- 需要外部类实例:创建内部类需要外部类实例
静态内部类
定义
静态内部类是用 static 修饰的内部类。
public class Outer {
private static String staticField = "静态字段";
private String instanceField = "实例字段";
// 静态内部类
public static class StaticInner {
public void display() {
System.out.println(staticField); // 可以访问静态成员
// System.out.println(instanceField); // ❌ 不能访问实例成员
}
public static void staticMethod() {
System.out.println("静态内部类的静态方法");
}
}
}
// 使用:不需要外部类实例
Outer.StaticInner inner = new Outer.StaticInner();
inner.display();
Outer.StaticInner.staticMethod();
特点
- 可以访问外部类静态成员:不能访问实例成员
- 可以有静态成员:可以有静态变量和静态方法
- 不需要外部类实例:可以直接创建
局部内部类
定义
局部内部类是定义在方法或代码块中的类。
public class Outer {
private String outerField = "外部类字段";
public void method() {
final String localVar = "局部变量"; // 必须是 final 或 effectively final
// 局部内部类
class LocalInner {
public void display() {
System.out.println(outerField); // 可以访问外部类成员
System.out.println(localVar); // 可以访问局部变量(必须是 final)
}
}
LocalInner inner = new LocalInner();
inner.display();
}
}
特点
- 作用域限制:只能在定义它的方法或代码块中使用
- 可以访问外部类成员:包括私有成员
- 可以访问局部变量:但必须是 final 或 effectively final
- 不能有访问修饰符:局部内部类不能有 public、private 等修饰符
匿名内部类
定义
匿名内部类是没有名字的内部类,通常用于实现接口或继承类。
// 接口
public interface Animal {
void makeSound();
}
// 使用匿名内部类
Animal animal = new Animal() {
@Override
public void makeSound() {
System.out.println("匿名内部类实现");
}
};
animal.makeSound();
实现接口
public interface Runnable {
void run();
}
// 匿名内部类实现接口
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("执行任务");
}
};
task.run();
继承类
public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 匿名内部类继承类
Animal animal = new Animal() {
@Override
public void makeSound() {
System.out.println("匿名内部类重写方法");
}
};
animal.makeSound();
Lambda 表达式替代
Java 8+ 可以使用 Lambda 表达式替代匿名内部类:
// 匿名内部类
Runnable task1 = new Runnable() {
@Override
public void run() {
System.out.println("任务");
}
};
// Lambda 表达式(更简洁)
Runnable task2 = () -> System.out.println("任务");
内部类的使用场景
1. 逻辑分组
将相关的类组织在一起:
public class LinkedList {
// 节点内部类
private class Node {
Object data;
Node next;
Node(Object data) {
this.data = data;
}
}
private Node head;
// ...
}
2. 访问控制
内部类可以访问外部类的私有成员:
public class Outer {
private String secret = "秘密";
public class Inner {
public void accessSecret() {
System.out.println(secret); // 可以访问私有成员
}
}
}
3. 回调函数
使用匿名内部类实现回调:
public class Button {
private OnClickListener listener;
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
public void click() {
if (listener != null) {
listener.onClick();
}
}
public interface OnClickListener {
void onClick();
}
}
// 使用匿名内部类
Button button = new Button();
button.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick() {
System.out.println("按钮被点击");
}
});
实际示例
示例 1:成员内部类
public class BankAccount {
private double balance;
// 成员内部类:交易记录
public class Transaction {
private String type;
private double amount;
private String timestamp;
public Transaction(String type, double amount) {
this.type = type;
this.amount = amount;
this.timestamp = java.time.LocalDateTime.now().toString();
}
public void display() {
System.out.println("类型:" + type);
System.out.println("金额:" + amount);
System.out.println("余额:" + balance); // 访问外部类成员
System.out.println("时间:" + timestamp);
}
}
public void deposit(double amount) {
balance += amount;
Transaction transaction = new Transaction("存款", amount);
transaction.display();
}
}
// 使用
BankAccount account = new BankAccount();
account.deposit(1000);