内部类
Java 中的内部类(Inner Class)是定义在另一个类(外部类)内部的类,共分为 4 种类型,核心区别在于「定义位置」和「是否有 static 修饰」,各自的语法、特性和适用场景差异明显。
一、4 种内部类分类(按常用程度排序)
1. 成员内部类(Member Inner Class)
定义:
直接定义在 外部类的类体中(方法外、代码块外),无 static 修饰,属于外部类的 “成员”(与外部类的字段、方法同级)。
核心特性:
- 依赖外部类实例:必须先创建外部类实例,才能通过外部类实例创建成员内部类实例(
外部类实例.new 内部类名()); - 访问权限:可访问外部类的 所有成员(包括
private字段 / 方法),通过外部类名.this引用外部类实例;外部类也能访问成员内部类的所有成员(包括private); - 访问修饰符:可加
public/private/protected/ 默认访问修饰符(控制外部类外的访问权限); - 不能定义 static 成员:成员内部类依赖外部类实例,而 static 成员独立于实例,因此不能有
static字段、static方法(static final常量除外)。
语法示例:
public class Outer {
private String outerField = "外部类字段";
// 成员内部类(无 static,外部类的成员)
public class MemberInner {
private String innerField = "成员内部类字段";
public void innerMethod() {
// 访问外部类成员(直接访问,或通过 Outer.this 显式访问)
System.out.println(outerField);
System.out.println(Outer.this.outerField); // 显式引用外部类实例
}
}
public static void main(String[] args) {
// 1. 先创建外部类实例
Outer outer = new Outer();
// 2. 通过外部类实例创建成员内部类实例
Outer.MemberInner inner = outer.new MemberInner();
inner.innerMethod(); // 输出:外部类字段 / 外部类字段
}
}
适用场景:
需要与外部类实例紧密关联,且需要访问外部类非静态成员的场景(如外部类的辅助类,与外部类逻辑强相关)。
2. 静态内部类(Static Nested Class)
定义:
直接定义在外部类的类体中,用 static 修饰的内部类(注意:官方称为 “静态嵌套类”,而非 “静态内部类”,但日常开发中常统称)。
核心特性:
- 不依赖外部类实例:可直接通过
外部类名.内部类名创建实例(无需外部类实例),与外部类是 “平级” 关系; - 访问权限:仅能访问外部类的 静态成员(
static字段 / 方法),不能访问非静态成员(无外部类实例引用); - 访问修饰符:可加
public/private/protected/ 默认修饰符(控制外部类外的访问权限); - 可定义 static 成员:静态内部类本身独立于外部类实例,因此可包含
static字段、static方法、static内部类。
语法示例:
public class Outer {
private static String staticOuterField = "外部类静态字段";
private String nonStaticField = "外部类非静态字段";
// 静态内部类(有 static 修饰)
public static class StaticInner {
private static String staticInnerField = "静态内部类静态字段";
public void innerMethod() {
// 可访问外部类静态成员
System.out.println(staticOuterField);
// 不能访问外部类非静态成员(编译报错)
// System.out.println(nonStaticField);
}
}
public static void main(String[] args) {
// 直接创建静态内部类实例(无需外部类实例)
Outer.StaticInner inner = new Outer.StaticInner();
inner.innerMethod(); // 输出:外部类静态字段
// 访问静态内部类的静态成员
System.out.println(Outer.StaticInner.staticInnerField);
}
}
适用场景:
与外部类相关但独立于外部类实例的场景(如外部类的工具类、辅助类,无需访问外部类非静态成员),例如 HashMap.Node(HashMap 的静态内部类,用于存储键值对)。
3. 局部内部类(Local Inner Class)
定义:
定义在 外部类的方法体或代码块(如 if/for 块)内部 的内部类,无访问修饰符(本质是 “局部变量级别的类”)。
核心特性:
- 作用域受限:仅在定义它的方法 / 代码块内有效,外部(包括外部类的其他方法)无法访问;
- 依赖外部类实例:创建局部内部类实例时,隐含持有外部类实例的引用(可访问外部类所有成员);
- 访问局部变量:仅能访问所在方法 / 代码块中
final或 effectively final(编译期无修改行为)的局部变量; - 结构完整:有明确类名,可定义构造器、字段、方法(区别于匿名内部类);
- 不能有 static 成员:局部内部类依赖方法栈帧,无法持有 static 成员(
static final常量除外)。
语法示例:
public class Outer {
private String outerField = "外部类字段";
public void outerMethod() {
// effectively final 局部变量(未被重新赋值)
String localVar = "方法局部变量";
final int finalVar = 100;
// 局部内部类(方法内定义,无访问修饰符)
class LocalInner {
public void innerMethod() {
// 访问外部类成员
System.out.println(outerField);
// 访问 final/effectively final 局部变量
System.out.println(localVar + " / " + finalVar);
}
}
// 仅在当前方法内创建实例并使用
LocalInner inner = new LocalInner();
inner.innerMethod(); // 输出:外部类字段 / 方法局部变量 / 100
}
public static void main(String[] args) {
new Outer().outerMethod();
}
}
适用场景:
方法内需要复用复杂逻辑(需字段、构造器、多方法),且逻辑封闭性要求高(不希望被外部访问)的场景(如方法内多次创建同一类型对象,且对象需维护状态)。
4. 匿名内部类(Anonymous Inner Class)
定义:
定义在方法 / 代码块内,无明确类名 的内部类(编译器自动生成类名,如 Outer$1.class),本质是 “没有名字的局部内部类”。
核心特性:
- 作用域:同局部内部类(仅在所在方法 / 代码块内有效);
- 继承 / 实现限制:必须 继承一个类或实现一个接口(单继承 / 单实现),不能同时继承类和实现多个接口;
- 无构造器:不能定义构造器(仅能通过父类构造器传参);
- 访问规则:与局部内部类一致(可访问外部类所有成员,仅能访问
final/effectively final 局部变量); - 仅能创建一个实例:匿名内部类定义时必须同时创建实例(不能单独定义类后后续创建多个实例)。
语法示例:
public class Outer {
private String outerField = "外部类字段";
public void outerMethod() {
final String localVar = "局部变量";
// 匿名内部类:实现 Runnable 接口,同时创建实例
Runnable runnable = new Runnable() {
@Override
public void run() {
// 访问外部类成员
System.out.println(outerField);
// 访问 final 局部变量
System.out.println(localVar);
}
};
runnable.run(); // 输出:外部类字段 / 局部变量
}
public static void main(String[] args) {
new Outer().outerMethod();
}
}
适用场景:
方法内仅需使用一次的简单逻辑(如回调函数、监听器),无需复用,且逻辑简单(无需多字段 / 方法),例如 Swing 组件监听器、线程任务(Runnable)。
二、4 种内部类核心区别对比表
| 对比维度 | 成员内部类 | 静态内部类 | 局部内部类 | 匿名内部类 |
|---|---|---|---|---|
| 定义位置 | 外部类体中(方法外) | 外部类体中(方法外) | 方法体 / 代码块内 | 方法体 / 代码块内 |
| 修饰符 | 可加 public/private 等 | 必须加 static,可加访问修饰符 | 无访问修饰符 | 无访问修饰符 |
| 依赖外部类实例 | 是(必须通过外部类实例创建) | 否(直接通过外部类名创建) | 是(隐含持有外部类实例) | 是(隐含持有外部类实例) |
| 访问外部类成员 | 所有成员(静态 + 非静态) | 仅静态成员 | 所有成员(静态 + 非静态) | 所有成员(静态 + 非静态) |
| 访问局部变量(方法内) | 不涉及(定义在方法外) | 不涉及(定义在方法外) | final/effectively final | final/effectively final |
| 类名 | 有明确类名 | 有明确类名 | 有明确类名 | 无(编译器生成) |
| 构造器 | 可定义 | 可定义 | 可定义 | 无(仅能通过父类构造器传参) |
| 继承 / 实现 | 可继承类 + 实现多个接口 | 可继承类 + 实现多个接口 | 可继承类 + 实现多个接口 | 仅能继承一个类或实现一个接口 |
| static 成员(自身) | 不能有(static final 除外) | 可以有(静态字段 / 方法) | 不能有(static final 除外) | 不能有(static final 除外) |
| 复用性 | 可多次创建实例 | 可多次创建实例 | 方法内可多次创建实例 | 仅能创建一个实例 |
三、总结
Java 内部类的核心是「根据定义位置和 static 修饰符区分类型」,选择时可遵循:
- 若需与外部类实例关联、访问非静态成员 → 成员内部类;
- 若与外部类实例无关、仅需访问静态成员 → 静态内部类;
- 若方法内需要复用复杂逻辑(多字段 / 构造器) → 局部内部类;
- 若方法内仅用一次简单逻辑(回调 / 监听器) → 匿名内部类。
内部类的核心价值是「封装性」和「关联性」—— 将与外部类强相关的逻辑封装在内部,既保证代码整洁,又能灵活访问外部类成员。