背景与动机
内部类是定义在另一个类内部的类。它仍然是外部类的成员,可以直接访问外部类的私有成员,也常用于回调、Builder、只服务某个外部类的辅助类型。
学内部类时,先分清四件事:
- 它是静态的还是非静态的
- 它定义在外部类的什么位置
- 创建内部类对象时,外部类实例是否必须存在
- 它和外部类、局部变量同名时,变量到底指向谁
核心原理拆解
1. 内部类是什么
class Outer { private int value = 10;
class Inner { void print() { System.out.println(value); } }}Inner 写在 Outer 里面,属于 Outer 的成员之一。成员内部类可以直接访问外部类的实例成员,包括 private 字段。
2. 四种内部类
| 类型 | 定义位置 | 是否依赖外部类实例 | 典型创建方式 |
|---|---|---|---|
| 静态内部类 | 类中,带 static | 否 | new Outer.StaticInner() |
| 成员内部类 | 类中,非 static | 是 | outer.new Inner() |
| 局部内部类 | 方法或代码块中 | 看所在方法 | 方法内 new Inner() |
| 匿名内部类 | 表达式位置 | 看上下文 | new Interface() { ... } |

3. 静态内部类
class Outer { private static int count = 0;
static class Counter { void show() { System.out.println(count); } }}
Outer.Counter counter = new Outer.Counter();特点:
- 不持有外部类实例引用
- 只能直接访问外部类的静态成员
- 创建时不需要先有
Outer对象
它和 Java static 关键字 的关系很紧,可以把它理解成“挂在类上的嵌套类”。

4. 成员内部类
Outer outer = new Outer();Outer.Inner inner = outer.new Inner();inner.print();特点:
- 每个成员内部类对象都隐式关联一个外部类实例
- 可以访问外部类的静态和非静态成员
- 内部类里可通过
Outer.this访问外部类当前对象

5. 局部内部类
class Outer { void demo() { class Local { void hello() { System.out.println("local"); } } new Local().hello(); }}特点:
- 只在定义它的方法或代码块内可见
- 定义在实例方法里时,可访问外部类实例成员
- 定义在静态方法里时,只能访问外部类静态成员

6. 匿名内部类
Runnable task = new Runnable() { @Override public void run() { System.out.println("run"); }};特点:
- 没有类名,必须继承一个类或实现一个接口
- 适合一次性回调,比如旧版 GUI 监听、Comparator
- 不能定义静态成员(Java 8 前规则更严,现代代码更推荐 Lambda)

7. 为什么局部变量要 effectively final
class Outer { void demo() { int n = 10; Runnable r = () -> System.out.println(n); }}局部变量存在栈上,方法结束后就销毁;内部类或 Lambda 可能晚于方法结束才执行。Java 要求捕获的局部变量在赋值后不能再改,避免“外部变量已经没了,内部还在用旧值”的生命周期问题。
Java 8 起不要求手写 final,但变量必须是 effectively final(赋值后不再修改)。
8. 同名变量怎么区分
class Outer { private int age = 12;
class Inner { private int age = 13;
void print() { int age = 14; System.out.println(age); // 14,局部变量 System.out.println(this.age); // 13,内部类成员 System.out.println(Outer.this.age); // 12,外部类成员 } }}优先级:局部变量 > 内部类成员 > 外部类成员。
最小可运行代码示例
public class InnerClassDemo { public static void main(String[] args) { Outer outer = new Outer(); outer.new Inner().print();
Outer.StaticHelper helper = new Outer.StaticHelper(); helper.show(); }}
class Outer { private int value = 100; private static int staticValue = 200;
class Inner { void print() { System.out.println(value); } }
static class StaticHelper { void show() { System.out.println(staticValue); } }}常见陷阱与错误示例
1. 把成员内部类当成静态内部类创建
// 编译错误:成员内部类不能 Outer.Inner 这样直接 new// Outer.Inner inner = new Outer.Inner();成员内部类必须先有外部类实例:outer.new Inner()。
2. 在静态方法里直接 new 非静态内部类
静态方法里没有“当前外部类对象”,不能直接创建依赖外部类实例的成员内部类。
3. 匿名内部类滥用
逻辑复杂、需要复用或测试的类型,应写成独立类或 Lambda,不要堆在匿名内部类里。
面试高频问题
1. Java 内部类有哪几种
静态内部类、成员内部类、局部内部类、匿名内部类。
2. 静态内部类和成员内部类区别
静态内部类不依赖外部类实例,只能直接访问外部类静态成员;成员内部类依赖外部类实例,可访问外部类所有成员。
3. 为什么内部类访问局部变量时变量要 effectively final
因为局部变量生命周期短,内部类可能更晚执行;限制不可变,是为了保证捕获的是稳定拷贝值。
4. 内部类有什么优点
能访问外部类私有成员、增强封装、实现回调、把只服务外部类的辅助逻辑收在一起。
一句话总结
内部类的核心不是语法花样,而是“嵌套类型与外部类怎么绑定”:静态内部类绑定类,成员内部类绑定外部类实例,局部/匿名内部类则常用于临时逻辑和回调。