第37章

Java内部类和嵌套类

深入学习Java中的四种内部类类型:成员内部类、静态嵌套类、局部内部类和匿名类的使用方法和最佳实践

学习目标

内部类概述

内部类(Inner Class)是定义在另一个类内部的类。Java支持四种类型的内部类,每种都有其特定的用途和特点。内部类提供了更好的封装性,可以访问外部类的私有成员,并且在某些设计模式中非常有用。

成员内部类

  • 定义在外部类中的非静态类
  • 可以访问外部类的所有成员
  • 持有外部类的引用
  • 不能定义静态成员(除常量)

静态嵌套类

  • 使用static关键字修饰
  • 只能访问外部类的静态成员
  • 不持有外部类引用
  • 可以独立于外部类对象存在

局部内部类

  • 定义在方法、构造方法或代码块中
  • 只能在定义的作用域内使用
  • 可以访问局部变量(必须是final)
  • 不能使用访问修饰符

匿名类

  • 没有名字的局部内部类
  • 直接实现接口或继承类
  • 适合简单的一次性实现
  • 广泛用于事件处理和回调

成员内部类(Member Inner Class)

成员内部类是最常见的内部类类型,它定义在外部类中,可以访问外部类的所有成员,包括私有成员。

基本语法

public class OuterClass {
    private String outerField = "外部类字段";
    
    // 成员内部类
    public class InnerClass {
        private String innerField = "内部类字段";
        
        public void innerMethod() {
            // 可以直接访问外部类的私有成员
            System.out.println(outerField);
            System.out.println(innerField);
        }
    }
    
    public void createInner() {
        InnerClass inner = new InnerClass();
        inner.innerMethod();
    }
}

创建和使用

// 创建外部类对象
OuterClass outer = new OuterClass();

// 方式1:通过外部类对象创建内部类对象
OuterClass.InnerClass inner1 = outer.new InnerClass();

// 方式2:在外部类方法中创建
outer.createInner();

成员内部类特点

  • 访问权限:可以访问外部类的所有成员,包括私有成员
  • 外部类引用:持有外部类的隐式引用,可以通过 OuterClass.this 访问
  • 静态限制:不能定义静态成员(静态常量除外)
  • 创建条件:必须先创建外部类对象才能创建内部类对象

静态嵌套类(Static Nested Class)

静态嵌套类使用static关键字修饰,它不能访问外部类的实例成员,但可以独立于外部类对象存在。

基本语法

public class OuterClass {
    private static String staticField = "外部类静态字段";
    private String instanceField = "外部类实例字段";
    
    // 静态嵌套类
    public static class NestedClass {
        private String nestedField = "嵌套类字段";
        
        public void nestedMethod() {
            // 可以访问外部类的静态成员
            System.out.println(staticField);
            
            // 不能直接访问外部类的实例成员
            // System.out.println(instanceField); // 编译错误
            
            System.out.println(nestedField);
        }
    }
}

创建和使用

// 直接创建静态嵌套类对象(不需要外部类实例)
OuterClass.NestedClass nested = new OuterClass.NestedClass();
nested.nestedMethod();

静态嵌套类特点

  • 独立性:不持有外部类引用,可以独立创建对象
  • 访问限制:只能访问外部类的静态成员
  • 静态成员:可以定义静态成员和方法
  • 内存效率:不会阻止外部类被垃圾回收

局部内部类(Local Inner Class)

局部内部类定义在方法、构造方法或代码块中,只能在定义它的作用域内使用。

基本语法

public class OuterClass {
    private String outerField = "外部类字段";
    
    public void methodWithLocalClass() {
        final String localVar = "局部变量";
        
        // 局部内部类
        class LocalClass {
            private String localField = "局部内部类字段";
            
            public void localMethod() {
                // 可以访问外部类成员
                System.out.println(outerField);
                
                // 可以访问final局部变量
                System.out.println(localVar);
                
                System.out.println(localField);
            }
        }
        
        // 创建和使用局部内部类
        LocalClass local = new LocalClass();
        local.localMethod();
    }
}

局部变量访问限制

局部内部类只能访问final或事实上的final局部变量。这是因为局部变量在方法结束后会被销毁,而内部类对象可能仍然存在。Java通过复制变量值来解决这个问题,因此变量必须是不可变的。

匿名类(Anonymous Class)

匿名类是没有名字的局部内部类,通常用于实现接口或继承类的简单实现。

实现接口

// 定义接口
interface Greeting {
    void sayHello(String name);
}

public class AnonymousExample {
    public void useAnonymousClass() {
        // 匿名类实现接口
        Greeting greeting = new Greeting() {
            @Override
            public void sayHello(String name) {
                System.out.println("你好, " + name + "!");
            }
        };
        
        greeting.sayHello("张三");
    }
}

继承类

public class AnonymousExample {
    public void useAnonymousThread() {
        // 匿名类继承Thread类
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("匿名线程正在运行");
            }
        };
        
        thread.start();
    }
}

匿名类使用场景

  • 事件处理:GUI编程中的事件监听器
  • 回调函数:异步操作的回调实现
  • 函数式接口:简单的函数式接口实现(Java 8之前)
  • 一次性使用:只使用一次的简单实现

内部类类型对比

特性 成员内部类 静态嵌套类 局部内部类 匿名类
访问外部类实例成员 ✅ 是 ❌ 否 ✅ 是 ✅ 是
访问外部类静态成员 ✅ 是 ✅ 是 ✅ 是 ✅ 是
持有外部类引用 ✅ 是 ❌ 否 ✅ 是 ✅ 是
可以定义静态成员 ❌ 否(除常量) ✅ 是 ❌ 否 ❌ 否
独立创建对象 ❌ 否 ✅ 是 ❌ 否 ❌ 否
使用访问修饰符 ✅ 是 ✅ 是 ❌ 否 ❌ 否
访问局部变量 ❌ 否 ❌ 否 ✅ 是(final) ✅ 是(final)

完整代码演示

以下是一个综合示例,展示了所有四种内部类类型的使用:

/**
 * 内部类综合演示
 * 展示四种内部类类型的使用方法和特点
 */
public class InnerClassDemo {
    private String outerField = "外部类字段";
    private static String staticField = "外部类静态字段";
    
    // 1. 成员内部类
    public class MemberInnerClass {
        public void demonstrate() {
            System.out.println("成员内部类访问: " + outerField);
        }
    }
    
    // 2. 静态嵌套类
    public static class StaticNestedClass {
        public void demonstrate() {
            System.out.println("静态嵌套类访问: " + staticField);
        }
    }
    
    // 3. 局部内部类演示方法
    public void demonstrateLocalClass() {
        final String localVar = "局部变量";
        
        class LocalInnerClass {
            public void demonstrate() {
                System.out.println("局部内部类访问: " + outerField);
                System.out.println("局部内部类访问: " + localVar);
            }
        }
        
        LocalInnerClass local = new LocalInnerClass();
        local.demonstrate();
    }
    
    // 4. 匿名类演示方法
    public void demonstrateAnonymousClass() {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名类执行任务: " + outerField);
            }
        };
        
        task.run();
    }
    
    public static void main(String[] args) {
        InnerClassDemo demo = new InnerClassDemo();
        
        // 测试成员内部类
        InnerClassDemo.MemberInnerClass memberInner = demo.new MemberInnerClass();
        memberInner.demonstrate();
        
        // 测试静态嵌套类
        InnerClassDemo.StaticNestedClass staticNested = new InnerClassDemo.StaticNestedClass();
        staticNested.demonstrate();
        
        // 测试局部内部类
        demo.demonstrateLocalClass();
        
        // 测试匿名类
        demo.demonstrateAnonymousClass();
    }
}
💻 查看完整代码 - 在线IDE体验

内部类最佳实践

选择指南

推荐做法

// 1. 优先使用静态嵌套类
public class OuterClass {
    public static class Builder {
        // 建造者模式实现
    }
}

// 2. 合理使用匿名类
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // 简单的事件处理
    }
});

// 3. 成员内部类用于紧密关联
public class LinkedList {
    private class Node {
        // 节点实现,需要访问外部类成员
    }
}
  • 不需要访问外部类实例时使用静态嵌套类
  • 简单的接口实现使用匿名类
  • 紧密关联的辅助类使用成员内部类
  • 方法内的临时类使用局部内部类

应该避免

// 1. 不必要的成员内部类
public class OuterClass {
    public class UnnecessaryInner {
        // 不访问外部类成员,应该使用静态嵌套类
    }
}

// 2. 复杂的匿名类
Runnable task = new Runnable() {
    public void run() {
        // 大量复杂逻辑...
        // 应该使用命名类
    }
};
  • 不访问外部类实例时使用成员内部类
  • 复杂逻辑使用匿名类
  • 忽略内存泄漏风险
  • 过度嵌套内部类

性能和内存考虑

  • 内存泄漏:成员内部类持有外部类引用,可能导致外部类无法被垃圾回收
  • 创建开销:静态嵌套类创建开销最小,匿名类可能较大
  • 内存效率:静态嵌套类 > 局部内部类 > 成员内部类 > 匿名类
  • 访问效率:直接访问比通过引用访问更高效

实际应用场景

设计模式中的应用

// 1. 迭代器模式 - 成员内部类
public class MyList {
    private Node head;
    
    public Iterator iterator() {
        return new ListIterator();
    }
    
    private class ListIterator implements Iterator {
        private Node current = head;
        
        public boolean hasNext() {
            return current != null;
        }
        
        public T next() {
            T data = current.data;
            current = current.next;
            return data;
        }
    }
}

// 2. 建造者模式 - 静态嵌套类
public class Product {
    private String name;
    private double price;
    
    private Product(Builder builder) {
        this.name = builder.name;
        this.price = builder.price;
    }
    
    public static class Builder {
        private String name;
        private double price;
        
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
        
        public Builder setPrice(double price) {
            this.price = price;
            return this;
        }
        
        public Product build() {
            return new Product(this);
        }
    }
}

// 3. 事件处理 - 匿名类
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("按钮被点击");
    }
});

本章小结