Java 内部类
Java 内部类
Java 有四种内部类类型,核心差异在于是否依赖外部类实例和可访问的成员范围。
内部类类型关系:
四种类型对比
| 类型 | 关键字 | 依赖外部实例 | 可访问成员 | 典型场景 |
|---|---|---|---|---|
| 静态嵌套类 | static | 否 | 仅静态成员 | 工具类、Builder |
| 成员内部类 | 无 | 是 | 所有成员 | 需要访问外部状态 |
| 局部内部类 | 无 | 是 | 所有 + final 局部变量 | 封装临时逻辑 |
| 匿名内部类 | 无 | 是 | 所有 + final 局部变量 | 一次性回调实现 |
静态嵌套类
不持有外部类引用,创建不需要外部实例:
class Outer {
static class Nested { }
}
Outer.Nested nested = new Outer.Nested();优先使用静态嵌套类
如果内部类不需要访问外部类实例,始终用 static 修饰——避免隐式持有外部引用导致内存泄漏。
成员内部类
隐式持有外部类引用(OuterClass.this),可访问所有成员包括 private:
class Outer {
private String field = "外部字段";
class Inner {
void access() {
System.out.println(field); // 直接访问
System.out.println(Outer.this.field); // 显式访问
}
}
}
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();局部内部类
定义在方法内,作用域仅限方法。可访问外部类成员和方法中的 effectively final 变量(Java 8+)。
void method() {
class LocalInner {
void doSomething() { System.out.println("局部内部类"); }
}
new LocalInner().doSomething();
}匿名内部类
没有类名的一次性实现,常用于事件回调:
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击");
}
});Java 8+ 替代方案
函数式接口优先用 Lambda:() -> System.out.println("new")。需要访问 this 或实现多方法接口时才用匿名内部类。
内存泄漏风险
非静态内部类持有外部类引用,生命周期不匹配时导致泄漏:
class Activity {
// ❌ Handler 持有 Activity 引用,消息未处理完时 Activity 无法回收
private Handler handler = new Handler() { ... };
}注意
解决方案 使用静态内部类 + WeakReference:static class SafeHandler extends Handler + WeakReference<Activity>。
字节码层面
内部类编译后以 $ 分隔命名:Outer.class + Outer$Inner.class。匿名内部类用数字编号:Outer$1.class。
最佳实践
| 规则 | 说明 |
|---|---|
| 优先静态嵌套类 | 不需要访问外部实例时始终加 static |
| 避免长生命周期引用 | 非静态内部类 + 长生命周期对象 = 内存泄漏 |
| Lambda 替代匿名类 | 函数式接口单方法场景用 Lambda |
| 少用局部内部类 | 作用域过小,使用场景有限 |