内部类

一、内部类概述

内部类(Inner Class)是定义在另一个类内部的类。Java 中的内部类可以访问外部类的成员变量和方法,它使得代码更加灵活和简洁。

1.1 内部类的四种形式

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<String> list = new ArrayList<String>() {{
            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关键字

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 静态内部类

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 内部类的字节码分析

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 迭代器模式(经典应用)

/**
 * 自定义容器的迭代器实现
 * 成员内部类的最佳实践
 */
public class MyArrayList<E> {
    private Object[] elements;
    private int size;

    public MyArrayList() {
        elements = new Object[10];
    }

    public void add(E e) {
        elements[size++] = e;
    }

    // ========== 迭代器(成员内部类) ==========
    public Iterator<E> iterator() {
        return new Itr();  // 返回内部类实例
    }

    private class Itr implements Iterator<E> {
        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<String> list = new MyArrayList<>();
list.add("A");
list.add("B");
list.add("C");

Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
    // it.remove();  // 支持安全删除
}

3.2 Builder 模式(静态内部类)

/**
 * 建造者模式的标准实现
 * 静态内部类的典型应用
 */
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 事件监听器(匿名内部类)

/**
 * 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 单例模式(静态内部类)

/**
 * 静态内部类实现单例模式
 * 线程安全 + 延迟加载
 */
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 才加载)
	}
}

⬅️ java中的位运算技巧 🏠 00-Java ➡️ 03-JDK9-21新特性速览