一、前言什么是内部类在 Java 中我们可以将一个类 A 定义在另一个类 B 的内部那么类 A 就被称为内部类Inner Class类 B 则被称为外部类Outer Class。内部类是 Java 面向对象特性中非常灵活的设计它允许我们将逻辑上紧密相关的类封装在一起同时实现了更好的封装性、访问控制和代码复用。内部类共分为 4 种核心类型成员内部类静态内部类局部内部类匿名内部类一、成员内部类定义在外部类成员位置的内部类1. 定义与语法成员内部类是 定义在外部类的成员位置类中方法外的内部类它的地位和外部类的成员变量、成员方法完全一致。// 外部类 public class Outer { // 外部类成员变量 private String outerName 外部类成员; public int outerAge 18; // 成员内部类定义在外部类成员位置 public class Inner { // 内部类成员变量 private String innerName 内部类成员; // 内部类成员方法 public void show() { // 1. 内部类可以直接访问外部类的所有成员包括private System.out.println(外部类私有变量 outerName); System.out.println(外部类公共变量 outerAge); // 2. 访问内部类自己的成员 System.out.println(内部类变量 innerName); } } // 外部类成员方法 public void method() { // 外部类要访问内部类成员必须创建内部类对象 Inner inner new Inner(); System.out.println(inner.innerName); } }2. 核心特点访问权限内部类可以直接访问外部类的所有成员包括 private 修饰的私有成员外部类要访问内部类成员必须创建内部类对象。修饰符成员内部类可以使用public、private、protected、默认包访问权限等任意访问修饰符也可以用final、abstract修饰。3. 创建对象的方式成员内部类的对象必须依赖外部类对象才能创建语法如下// 方式1分步创建 Outer outer new Outer(); // 先创建外部类对象 Outer.Inner inner outer.new Inner(); // 再通过外部类对象创建内部类对象 // 方式2一步创建常用 Outer.Inner inner new Outer().new Inner(); // 调用内部类方法 inner.show();4. 变量访问的就近原则与 this 区分当内部类、外部类出现同名变量时遵循就近原则可以用this和外部类名.this区分public class Outer { private String name 外部类name; public class Inner { private String name 内部类name; public void show() { String name 局部变量name; // 1. 访问局部变量就近 System.out.println(name); // 2. 访问内部类成员变量 System.out.println(this.name); // 3. 访问外部类成员变量核心语法 System.out.println(Outer.this.name); } } }5. 使用场景当一个类只服务于外部类不需要被其他类单独使用时用成员内部类封装逻辑。类似于汽车与发动机或油箱之类的二、静态内部类用 static 修饰的成员内部类1. 定义与语法静态内部类是用static修饰的成员内部类它本质上相当于一个 “顶层类”只是被定义在了外部类内部。public class Outer { private static String staticName 外部类静态变量; private String instanceName 外部类实例变量; // 静态内部类 public static class StaticInner { public void show() { // 1. 只能直接访问外部类的静态成员不能直接访问实例成员 System.out.println(外部类静态变量 staticName); // System.out.println(instanceName); // 编译报错无法直接访问 // 2. 要访问外部类实例成员必须创建外部类对象 Outer outer new Outer(); System.out.println(外部类实例变量 outer.instanceName); } } }// 顶层类 A public class A { public static int x 100; // public static 成员 int y 10; } // 顶层类 B另一个独立的类 public class B { public static void main(String[] args) { System.out.println(A.x); // 合法static 成员 // System.out.println(A.y); // 报错非 static 不能从静态上下文引用 } }2. 核心特点不依赖外部类对象静态内部类不依赖外部类对象可以直接创建。访问限制只能直接访问外部类的静态成员不能直接访问外部类的实例成员需要创建外部类对象才能访问。因为静态内部类是 “独立” 的它不依附于外部类对象存在所以没有外部对象的引用不能直接访问属于对象的实例成员只能访问属于类的静态成员3. 创建对象的方式静态内部类的对象不需要依赖外部类对象直接创建即可// 直接创建静态内部类对象 Outer.StaticInner inner new Outer.StaticInner(); inner.show();4. 使用场景当内部类不需要访问外部类的实例成员仅作为外部类的工具类存在时使用。典型场景单例模式的静态内部类实现、工具类的嵌套封装。三、局部内部类定义在方法 / 代码块中的内部类1. 定义与语法局部内部类是定义在外部类的方法、构造方法、代码块等局部位置的内部类它的作用域仅限于当前方法 / 代码块内。public class Outer { private String outerName 外部类成员; public void method() { // 局部变量 String methodName 方法局部变量; // 局部内部类定义在方法内部 class LocalInner { private String innerName 局部内部类成员; public void show() { // 1. 可以直接访问外部类的所有成员 System.out.println(外部类变量 outerName); // 2. 可以直接访问方法的局部变量JDK 8 自动final System.out.println(方法局部变量 methodName); // 3. 访问内部类自己的成员 System.out.println(内部类变量 innerName); } } // 只能在当前方法内创建局部内部类对象 LocalInner inner new LocalInner(); inner.show(); } // 方法外不能用 LocalInner in new Inner(); // 报错找不到 }2. 核心特点作用域限制只能在定义它的方法 / 代码块内使用方法外完全无法访问。出了方法就被销毁了访问权限可以直接访问外部类的所有成员也可以访问方法的局部变量JDK 8 要求局部变量是final或 effectively final即不可修改。方法局部变量存在栈内存方法执行完 → 栈帧销毁 → 变量直接消失生命周期和方法绑定。局部内部类对象存在堆内存方法执行完后它可能还活着比如返回给外部、交给线程异步执行生命周期远长于方法。给内部类偷偷加一个私有成员变量用来存方法局部变量的副本给内部类的构造方法隐式传参把方法里的局部变量值赋值给这个副本。修饰符限制局部内部类不能使用访问修饰符public/private等都不允许也不能用static修饰只能用final/abstract修饰。因为它就是方法里的局部东西不需要权限修饰。局部内部类的作用域仅局限于定义它的方法 / 代码块内部出了方法就完全不可见因此「访问权限」这个概念本身就没有意义语法上直接禁止使用。3. 使用场景当一个类只需要在某个方法内使用一次不需要对外暴露时用局部内部类封装逻辑。典型场景方法内的临时工具类、一次性逻辑封装。四、匿名内部类没有名字的局部内部类最常用1. 定义与语法匿名内部类是没有类名的局部内部类它是内部类的简化写法核心作用是快速创建一个类 / 接口的子类 / 实现类对象无需显式定义类名。匿名内部类必须继承一个父类或实现一个接口语法格式new 父类/接口() { // 重写父类/接口的方法 Override public void 方法名() { // 方法体 } };2. 基础用法示例1继承父类的匿名内部类// 父类Animal class Animal { public void eat() { System.out.println(动物吃东西); } } public class Outer { public void method() { // 匿名内部类继承Animal重写eat方法 Animal dog new Animal() { Override public void eat() { System.out.println(狗吃骨头); } }; // 调用方法 dog.eat(); } }2实现接口的匿名内部类最常用// 接口Flyable interface Flyable { void fly(); } public class Outer { public void method() { // 匿名内部类实现Flyable接口 Flyable bird new Flyable() { Override public void fly() { System.out.println(鸟儿在飞翔); } }; bird.fly(); } }3. 核心特点一步到位定义类 创建对象 二合一代码简洁。必须重写方法必须重写父类 / 接口的所有抽象方法如果是抽象类重写抽象方法如果是接口重写所有抽象方法。作用域限制本质是局部内部类只能在定义的位置使用通常作为方法参数直接传递。分号规则赋值给变量时末尾必须加;完整语句结束直接作为方法参数时匿名类末尾不加;仅在方法调用末尾加;4. 作为方法参数的简化用法匿名内部类最常用的场景是直接作为方法参数传递无需创建变量// 方法接收Animal类型参数 public static void showAnimal(Animal a) { a.eat(); } public static void main(String[] args) { // 直接把匿名内部类作为参数传入 showAnimal( new Animal(){ Override public void eat() { System.out.println(猫吃鱼); } } ); // 方法调用末尾加;匿名类内部不加; }5. 关键注意事项不能定义构造方法因为没有类名无法定义构造方法只能用实例初始化块。只能创建一个对象匿名内部类只能创建一个对象无法复用。访问局部变量匿名内部类能访问外部类的所有成员并且和局部内部类一样只能访问final/effectively final 的局部变量。6. 使用场景只需要使用一次的类 / 接口实现避免写冗余的子类文件。典型场景Android 的按钮点击事件、Swing 的事件监听、线程Runnable接口实现、回调函数。六、4 种内部类核心对比表特性成员内部类静态内部类局部内部类匿名内部类定义位置外部类成员位置方法外外部类成员位置方法外外部类方法 / 代码块内方法 / 代码块内无类名static 修饰不能JDK16 允许不推荐必须用 static 修饰不能不能访问外部类成员可直接访问所有成员仅可直接访问静态成员可直接访问所有成员可直接访问所有成员创建对象方式依赖外部类对象不依赖外部类对象仅在方法内创建定义时直接创建访问修饰符支持任意访问修饰符支持任意访问修饰符不支持访问修饰符无类名无修饰符静态成员不能定义不推荐可自由定义不能定义不能定义作用域外部类内 / 外部可访问外部类内 / 外部可访问仅当前方法内仅定义位置使用复用性可复用可复用不可复用不可复用一次性编译后字节码Outer$Inner.classOuter$StaticInner.classOuter$1LocalInner.classOuter$1.class七、内部类的核心优势与使用总结1. 内部类的核心优势更好的封装性可以将仅服务于外部类的类隐藏在外部类内部不对外暴露。灵活的访问控制内部类可以直接访问外部类的私有成员实现了更细粒度的权限控制。代码简洁性匿名内部类避免了冗余的子类定义简化了代码结构。实现多继承内部类可以继承其他类间接让外部类实现 “多继承” 的效果。2. 开发中使用建议优先使用静态内部类如果内部类不需要访问外部类实例成员优先用静态内部类减少对象依赖。谨慎使用成员内部类仅当内部类强依赖外部类对象时使用避免滥用导致代码结构混乱。局部内部类尽量少用逻辑复杂时优先抽成独立类避免方法内代码臃肿。匿名内部类是高频用法事件监听、线程、回调等场景优先用匿名内部类八、常见面试题与避坑指南1. 常见面试题匿名内部类可以继承多个类吗答不可以匿名内部类只能继承一个类或实现一个接口Java 单继承限制。成员内部类可以有静态方法吗答JDK 16 之前不允许JDK 16 之后允许但不推荐因为成员内部类依赖外部类对象静态方法不依赖对象逻辑冲突。静态方法就跟上面讲的顶层类都可以直接调用不需要创建对象但是成员内部类的存在是依赖于外部类的。所以不能直接调用所以矛盾局部内部类为什么不能用访问修饰符答因为局部内部类的作用域仅限于方法内访问修饰符对外无效语法上不允许使用。匿名内部类的分号什么时候加答赋值给变量时加Animal a new Animal(){};作为方法参数时不加method(new Animal(){});九、结语内部类是 Java 面向对象编程中非常重要的特性4 种内部类各有适用场景成员内部类强依赖外部类的封装场景静态内部类独立于外部类的工具类场景局部内部类方法内一次性逻辑封装匿名内部类事件监听、回调等高频简化场景