--- title: "02-内部类" created: 2025-11-27 tags: - Java --- # 内部类 ## **一、内部类概述** **内部类(Inner Class)是定义在另一个类内部的类。Java 中的内部类可以访问外部类的成员变量和方法,它使得代码更加灵活和简洁。** ### **1.1 内部类的四种形式** ```java public class OuterClass { private String outerField = "outer"; private static String staticField = "static outer"; // ========== 1. 成员内部类(非静态内部类) ========== // 特点:持有外部类的引用(OuterClass.this) class InnerClass { private String innerField = "inner"; // ❌ static String staticInner = "error"; // 不能有静态成员(除编译时常量) static final int CONSTANT = 100; // ✅ 编译时常量例外 public void access() { // 可访问外部类的所有成员(包括 private) System.out.println(outerField); // ✅ System.out.println(staticField); // ✅ // 显式引用外部类实例 System.out.println(OuterClass.this.outerField); // 调用外部类方法 OuterClass.this.outerMethod(); } // 内部类与外部类同名变量的区分 public void shadowTest() { String outerField = "local"; // 局部变量 System.out.println(outerField); // local System.out.println(this.innerField); // inner System.out.println(OuterClass.this.outerField); // outer } } // ========== 2. 静态内部类(嵌套类) ========== // 特点:不持有外部类引用,相当于顶层类 static class StaticInnerClass { private String staticInnerField = "static inner"; private static String staticField2 = "static"; // ✅ 可有静态成员 public void access() { // ❌ System.out.println(outerField); // 编译错误:无法访问非静态成员 System.out.println(staticField); // ✅ 可访问外部类静态成员 // 需要外部类实例才能访问非静态成员 OuterClass outer = new OuterClass(); System.out.println(outer.outerField); // ✅ } public static void staticMethod() { System.out.println("static inner method"); } } // ========== 3. 局部内部类(方法内定义) ========== public void methodWithLocalInner() { final int finalVar = 10; // 显式 final int effectivelyFinalVar = 20; // JDK8+ 隐式 final(不能修改) // effectivelyFinalVar = 30; // ❌ 修改后,内部类无法访问 // 局部内部类:只在方法内可见 class LocalInner { private String localInnerField = "local inner"; public void access() { // 可访问:外部类成员 + 方法参数 + effectively final 局部变量 System.out.println(outerField); // ✅ 外部类成员 System.out.println(finalVar); // ✅ final 变量 System.out.println(effectivelyFinalVar); // ✅ effectively final System.out.println(localInnerField); // ✅ 自身成员 } } LocalInner local = new LocalInner(); local.access(); // ❌ 局部内部类在方法外不可见 // LocalInner error = new LocalInner(); // 编译错误 } // ========== 4. 匿名内部类(常用于回调) ========== public void anonymousExample() { // 4.1 实现接口 Runnable r1 = new Runnable() { @Override public void run() { System.out.println(outerField); // ✅ 访问外部类 } }; // 4.2 继承类 Thread t = new Thread() { @Override public void run() { System.out.println("anonymous thread"); } }; // 4.3 带初始化块的匿名类 List list = new ArrayList() {{ add("item1"); // 实例初始化块 add("item2"); }}; // 4.4 JDK8+ Lambda 替代(只适用于函数式接口) Runnable r2 = () -> System.out.println(outerField); // ✅ 更简洁 } private void outerMethod() { System.out.println("outer method"); } // ========== 创建内部类实例 ========== public static void main(String[] args) { OuterClass outer = new OuterClass(); // 1. 成员内部类:需要外部类实例 InnerClass inner = outer.new InnerClass(); // ✅ inner.access(); // 2. 静态内部类:不需要外部类实例 StaticInnerClass staticInner = new OuterClass.StaticInnerClass(); // ✅ staticInner.access(); StaticInnerClass.staticMethod(); // 直接调用静态方法 // 3. 局部内部类:只能在方法内创建 outer.methodWithLocalInner(); // 4. 匿名内部类:在创建处使用 outer.anonymousExample(); } } ``` --- ## **二、核心面试题详解** ### **2.1 为什么局部内部类访问的局部变量必须是** [[01-final关键字|01-final关键字]]**?** ```java public class FinalVariableDemo { /** * ❓ 问题:为什么局部内部类捕获的局部变量必须是 final(或 effectively final)? * * ✅ 答案:变量生命周期不一致 + 值拷贝机制 */ public Runnable createRunnable() { int localVar = 100; // 栈内存,方法结束后销毁 // 局部内部类 class LocalRunnable implements Runnable { @Override public void run() { // ⚠️ 此时 localVar 可能已经被销毁! System.out.println(localVar); // 如何访问? } } return new LocalRunnable(); } /** * 底层原理:编译器生成的字节码 * * javac 编译后生成两个 class 文件: * - FinalVariableDemo.class * - FinalVariableDemo$1LocalRunnable.class */ // 反编译后的内部类(简化版) class LocalRunnable_Generated implements Runnable { final int val$localVar; // ✅ 编译器添加的合成字段 LocalRunnable_Generated(int capturedVar) { this.val$localVar = capturedVar; // 构造时复制值 } @Override public void run() { System.out.println(val$localVar); // 使用拷贝的值 } } /** * 为什么必须 final? * * 1️⃣ 值拷贝机制: * - localVar 是方法栈上的变量 * - 内部类对象在堆上,生命周期可能比方法长 * - 编译器将 localVar 的值拷贝到内部类的字段中 * * 2️⃣ 保持一致性: * 如果允许修改 localVar,会出现: * - 方法内看到的是修改后的值 * - 内部类看到的是拷贝的旧值 * - 造成数据不一致! * * 3️⃣ 解决方案:强制 final * - final 变量不能修改 * - 保证内部类和外部方法看到的值一致 */ // ========== 示例:违反 final 规则 ========== public void demonstrateError() { int count = 0; Runnable r = new Runnable() { @Override public void run() { // ❌ 编译错误:Variable 'count' is accessed from within inner class, needs to be final or effectively final // System.out.println(count); } }; count++; // ⚠️ 修改后,count 不再是 effectively final // 解决方案 1:使用 final 包装类 final int[] counter = {0}; // 数组引用是 final,但元素可变 Runnable r2 = new Runnable() { @Override public void run() { System.out.println(counter[0]); // ✅ counter[0]++; // ✅ 修改数组元素 } }; // 解决方案 2:使用原子类 final AtomicInteger atomicCount = new AtomicInteger(0); Runnable r3 = () -> { System.out.println(atomicCount.get()); // ✅ atomicCount.incrementAndGet(); // ✅ }; } /** * JDK 8 新特性:effectively final * * - JDK 7 及之前:必须显式声明 final * - JDK 8 及之后:未显式 final,但未被修改,视为 effectively final */ public void effectivelyFinalDemo() { int jdk7Var = 10; // 隐式 final(未修改) Runnable r = () -> { System.out.println(jdk7Var); // ✅ JDK8+ 允许 }; // jdk7Var = 20; // ❌ 修改后编译失败 } } ``` ### **2.2 成员内部类 vs 静态内部类** ```java public class InnerClassComparison { private String outerField = "outer"; private static String staticField = "static"; // ========== 成员内部类 ========== class MemberInner { // 特点 1:隐式持有外部类引用 public void showReference() { System.out.println(OuterClass.this); // OuterClass@hashcode } // 特点 2:可访问外部类所有成员 public void accessOuter() { System.out.println(outerField); // ✅ System.out.println(staticField); // ✅ } // 特点 3:不能有静态成员(除常量) // static int staticVar = 1; // ❌ 编译错误 static final int CONSTANT = 1; // ✅ 编译时常量 // JDK 16+ 新特性:允许静态成员 // static void staticMethod() {} // ✅ JDK 16+ } // ========== 静态内部类 ========== static class StaticInner { // 特点 1:不持有外部类引用 // 无法访问 OuterClass.this // 特点 2:只能访问外部类静态成员 public void accessOuter() { // System.out.println(outerField); // ❌ System.out.println(staticField); // ✅ } // 特点 3:可以有静态成员 static int staticVar = 1; // ✅ static void staticMethod() {} // ✅ } // ========== 内存占用对比 ========== public static void memoryTest() { OuterClass outer = new OuterClass(); // 成员内部类:持有外部类引用 MemberInner member = outer.new MemberInner(); // 内存结构: // MemberInner { // this$0 -> OuterClass // 隐藏字段,4-8 字节 // ... 其他字段 // } // 静态内部类:无额外引用 StaticInner staticInner = new StaticInner(); // 内存结构: // StaticInner { // ... 仅自身字段 // } /** * 内存泄漏风险: * * 如果 MemberInner 生命周期长于 OuterClass, * 会导致 OuterClass 无法被 GC 回收! */ } // ========== 使用场景对比 ========== /** * 使用成员内部类: * 1. 需要访问外部类实例状态 * 2. 如:迭代器、事件监听器 */ class Iterator { private int cursor = 0; public boolean hasNext() { return cursor < outerField.length(); // ✅ 访问外部类 } } /** * 使用静态内部类: * 1. 不需要访问外部类实例 * 2. 如:Builder 模式、辅助类 * 3. 优先使用(避免内存泄漏) */ static class Builder { private String field1; private String field2; public Builder setField1(String field1) { this.field1 = field1; return this; } public OuterClass build() { OuterClass instance = new OuterClass(); instance.outerField = this.field1; return instance; } } } ``` ### **2.3 内部类的字节码分析** ```java public class BytecodeAnalysis { private String outer = "outer"; class Inner { public void print() { System.out.println(outer); } } /** * 编译后生成的文件: * - BytecodeAnalysis.class * - BytecodeAnalysis$Inner.class * * javap -c -p BytecodeAnalysis$Inner.class 反编译结果(简化): */ /* class BytecodeAnalysis$Inner { // 1️⃣ 编译器生成的合成字段(持有外部类引用) final BytecodeAnalysis this$0; // 2️⃣ 编译器生成的构造器(传入外部类引用) BytecodeAnalysis$Inner(BytecodeAnalysis outer) { this.this$0 = outer; } // 3️⃣ print 方法的字节码 public void print() { // 实际访问:this$0.outer System.out.println(this.this$0.outer); } } */ /** * 关键点: * - this$0 是编译器自动添加的 synthetic 字段 * - final 修饰,防止被修改 * - 构造时传入外部类实例 */ // ========== 多层嵌套的内部类 ========== class Level1 { private String level1Field = "L1"; class Level2 { private String level2Field = "L2"; class Level3 { public void access() { // 访问外层类需要多次跳转 System.out.println(outer); // BytecodeAnalysis.this.outer System.out.println(level1Field); // Level1.this.level1Field System.out.println(level2Field); // Level2.this.level2Field System.out.println(Level1.this.level1Field); // 显式访问 } } } } /** * 反编译 Level3.class: * * class BytecodeAnalysis$Level1$Level2$Level3 { * final BytecodeAnalysis$Level1$Level2 this$2; // 持有 Level2 引用 * * BytecodeAnalysis$Level1$Level2$Level3(BytecodeAnalysis$Level1$Level2 level2) { * this.this$2 = level2; * } * * public void access() { * // 访问 outer:this$2.this$1.this$0.outer * // 访问 level1Field:this$2.this$1.level1Field * // 访问 level2Field:this$2.level2Field * } * } * * ⚠️ 性能问题:多层嵌套导致多次间接访问 */ } ``` ## **三、实战应用场景** ### **3.1 迭代器模式(经典应用)** ```java /** * 自定义容器的迭代器实现 * 成员内部类的最佳实践 */ public class MyArrayList { private Object[] elements; private int size; public MyArrayList() { elements = new Object[10]; } public void add(E e) { elements[size++] = e; } // ========== 迭代器(成员内部类) ========== public Iterator iterator() { return new Itr(); // 返回内部类实例 } private class Itr implements Iterator { int cursor = 0; // 当前位置 int lastRet = -1; // 上次返回的位置 int expectedModCount = modCount; // 快速失败检测 @Override public boolean hasNext() { return cursor != size; // ✅ 访问外部类的 size } @Override @SuppressWarnings("unchecked") public E next() { checkForComodification(); int i = cursor; if (i >= size) throw new NoSuchElementException(); Object[] elementData = MyArrayList.this.elements; // ✅ 访问外部类数组 if (i >= elementData.length) throw new ConcurrentModificationException(); cursor = i + 1; return (E) elementData[lastRet = i]; } @Override public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { MyArrayList.this.remove(lastRet); // ✅ 调用外部类方法 cursor = lastRet; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } // 修改计数(用于快速失败) private transient int modCount = 0; public void remove(int index) { modCount++; // 删除逻辑... } /** * 为什么迭代器使用成员内部类? * * 1️⃣ 需要访问外部类的 private 成员(elements、size) * 2️⃣ 逻辑紧密耦合,不需要暴露给外部 * 3️⃣ 每个容器实例有独立的迭代器状态 */ } // 使用示例 MyArrayList list = new MyArrayList<>(); list.add("A"); list.add("B"); list.add("C"); Iterator it = list.iterator(); while (it.hasNext()) { System.out.println(it.next()); // it.remove(); // 支持安全删除 } ``` ### **3.2 Builder 模式(静态内部类)** ```java /** * 建造者模式的标准实现 * 静态内部类的典型应用 */ public class User { // 必填字段 private final String username; private final String password; // 可选字段 private final String email; private final String phone; private final int age; private final String address; // ========== 私有构造器,只能通过 Builder 创建 ========== private User(Builder builder) { this.username = builder.username; this.password = builder.password; this.email = builder.email; this.phone = builder.phone; this.age = builder.age; this.address = builder.address; } // ========== Builder(静态内部类) ========== public static class Builder { // 必填字段 private final String username; private final String password; // 可选字段(默认值) private String email = ""; private String phone = ""; private int age = 0; private String address = ""; // 必填字段通过构造器传入 public Builder(String username, String password) { this.username = username; this.password = password; } // 可选字段通过链式调用设置 public Builder email(String email) { this.email = email; return this; } public Builder phone(String phone) { this.phone = phone; return this; } public Builder age(int age) { if (age < 0 || age > 150) { throw new IllegalArgumentException("Invalid age"); } this.age = age; return this; } public Builder address(String address) { this.address = address; return this; } // 构建最终对象 public User build() { // 可在此处添加验证逻辑 if (username == null || username.isEmpty()) { throw new IllegalStateException("Username is required"); } return new User(this); } } /** * 为什么 Builder 使用静态内部类? * * 1️⃣ 不需要访问外部类实例(User 未创建前就要使用 Builder) * 2️⃣ 逻辑内聚,Builder 只为 User 服务 * 3️⃣ 避免内存泄漏(无外部类引用) * 4️⃣ 可以访问外部类的 private 构造器 */ @Override public String toString() { return "User{" + "username='" + username + '\'' + ", email='" + email + '\'' + ", phone='" + phone + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } } // 使用示例 User user = new User.Builder("admin", "123456") .email("admin@example.com") .phone("1234567890") .age(25) .address("Beijing") .build(); System.out.println(user); /** * Builder 模式优势: * 1. 参数众多时避免构造器膨胀 * 2. 可读性强(链式调用) * 3. 不可变对象(所有字段 final) * 4. 参数校验集中在 build() 方法 */ ``` ### **3.3 事件监听器(匿名内部类)** ```java /** * GUI 编程中的事件处理 * 匿名内部类的经典场景 */ public class ButtonDemo { public static void main(String[] args) { JFrame frame = new JFrame("Button Demo"); JButton button = new JButton("Click Me"); // ========== 方式 1:匿名内部类(JDK 7) ========== button.addActionListener(new ActionListener() { private int clickCount = 0; // 内部状态 @Override public void actionPerformed(ActionEvent e) { clickCount++; System.out.println("Clicked " + clickCount + " times"); // ✅ 可访问外部变量(effectively final) System.out.println("Button: " + button.getText()); } }); // ========== 方式 2:Lambda 表达式(JDK 8+) ========== // ⚠️ Lambda 无法定义实例变量 AtomicInteger count = new AtomicInteger(0); // 替代方案 button.addActionListener(e -> { count.incrementAndGet(); System.out.println("Clicked " + count.get() + " times"); }); // ========== 方式 3:方法引用(JDK 8+) ========== button.addActionListener(System.out::println); frame.add(button); frame.setSize(300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } /** * 匿名内部类 vs Lambda: * * 匿名内部类: * ✅ 可以有实例变量和初始化块 * ✅ 可以实现多个方法(非函数式接口) * ✅ 可以覆盖 Object 方法(toString/equals/hashCode) * ❌ 语法冗长 * * Lambda: * ✅ 语法简洁 * ✅ 无额外类文件(invokedynamic) * ❌ 只能用于函数式接口 * ❌ 无法有实例状态 */ } ``` ### **3.4 单例模式(静态内部类)** ```java /** * 静态内部类实现单例模式 * 线程安全 + 延迟加载 */ public class Singleton { // 私有构造器 private Singleton() { // 防止反射攻击 if (Holder.INSTANCE != null) { throw new IllegalStateException("Singleton already initialized"); } } // ========== 静态内部类(延迟初始化持有者) ========== private static class Holder { private static final Singleton INSTANCE = new Singleton(); } // 获取单例 public static Singleton getInstance() { return Holder.INSTANCE; // 首次调用时才加载 Holder 类 } /** * 为什么使用静态内部类? * * 1️⃣ 延迟加载: * - Singleton 类加载时,Holder 不会加载 * - 首次调用 getInstance() 时才加载 Holder * - 利用 JVM 类加载机制保证线程安全 * * 2️⃣ 线程安全: * - JVM 保证类初始化过程的线程安全 * - 无需 synchronized * * 3️⃣ 高性能: * - 无锁竞争 * - 无 volatile 开销 * * 4️⃣ 对比其他方案: * - 饿汉式:类加载时初始化,无延迟加载 * - DCL(双检锁):需要 volatile,复杂易错 * - 枚举:最佳方案,但语义不直观 */ // 业务方法 public void doSomething() { System.out.println("Singleton instance: " + this); } } // 使用示例 Singleton s1 = Singleton.getInstance(); Singleton s2 = Singleton.getInstance(); System.out.println(s1 == s2); // true /** * 类加载时机验证: */ class LoadingTest { static { System.out.println("Singleton class loaded"); } static class Holder { static { System.out.println("Holder class loaded"); } static final Singleton INSTANCE = new Singleton(); } public static void main(String[] args) { System.out.println("Main start"); // 输出:Main start // Singleton class loaded(Singleton 类被引用) Singleton instance = Holder.INSTANCE; // 输出:Holder class loaded(首次访问 Holder 才加载) } } ``` --- ⬅️ [[01-java中的位运算技巧|java中的位运算技巧]] 🏠 [[00-Java|00-Java]] ➡️ [[03-JDK9-21新特性速览|03-JDK9-21新特性速览]]