Java中,内部类(Inner Class)是定义在另一个类的内部的类。这种结构使得内部类可以很方便地访问外部类的私有成员,同时对外界隐藏。内部类是Java语言的一个强大特性,提供了更好的封装和设计灵活性。内部类主要有四种类型:成员内部类、局部内部类、匿名内部类和静态内部类。

1、非静态内部类

非静态内部类,也就是成员内部类,是定义在另一个类内部的非静态类。这种内部类与外部类之间有着密切的联系,它可以访问外部类的所有成员(包括私有成员),同时外部类也可以访问内部类的所有成员(包括私有成员)。

public class Main {
  private int outerValue = 100;
  public static void main(String[] args) {
    Main outer = new Main();
    Main.InnerClass inner = outer.new InnerClass();
    inner.display();
    System.exit(0); //success
  }

  class InnerClass {
      private int innerValue = 200;
         
      public void display() {
          System.out.println("Outer Value: " + outerValue); 
          //     访问外部类成员
          System.out.println("Inner Value: " + innerValue); 
          // 访问内部类成员
      }
  }
}

注意:同一个java源文件定义了多个类,它们之间是相互独立的,那种情况不是内部类。

2、静态内部类

Java中的静态内部类(Static Nested Class)是定义在另一个类内部的静态类。由于是静态的,这种内部类与外部类的实例无关,并且不能直接访问外部类的非静态成员和方法。可以访问外部类的静态成员,包括私有的静态成员。静态内部类的独立性使其成为实现与外部类相关但又可独立存在。静态内部类在Java中的应用场景包括逻辑分组(将相关的类组织在一起),封装(隐藏内部类的细节,保持外部类的清洁接口),以及减少内存占用(静态内部类不持有对外部类实例的引用)。它们在设计模式中也很有用。

public class Main {
 private static String staticVar = "外部静态变量";

    static class StaticNestedClass {
        public void display() {
            System.out.println("访问 outer class static variable: " + staticVar);
        }
    }

    public static void main(String[] args) {
        Main.StaticNestedClass nestedObject = new Main.StaticNestedClass();
        nestedObject.display();
    }
}

3、局部内部类

局部内部类(Local Inner Class)是定义在一个方法或任意作用域内的类。它对于实现复杂的逻辑非常有用,尤其是当这些逻辑仅在定义它的块内部需要时。局部内部类对外部完全隐藏,只能在定义它的作用域内被使用。局部内部类可以访问其外围方法的局部变量(Java 8之后,即使这些变量不是final的也可以访问)和外部类的所有成员。这些类不能有静态成员,因为它们与外部世界隔离。局部内部类主要用于实现那些仅在某个方法或作用域内使用的类,以及在需要实现或继承特定接口或类且其使用非常局限的情况下。

public class Main {
  
   public static void main(String[] args) {
        Main outer = new Main();
        outer.display();
    }
    public void display() {
        // 局部内部类在方法内定义
        class LocalInnerClass {
            public void print() {
                System.out.println("局部内部类");
            }
        }

        // 局部内部类的对象创建和使用
        LocalInnerClass localInnerClass = new LocalInnerClass();
        localInnerClass.print();
    }
    

}

4、匿名内部类

匿名内部类是一种没有名称的局部内部类。它们通常用于实现接口或继承某个类的同时,创建并实例化一个对象。匿名内部类对于编写事件监听器、实现回调等场景特别有用。匿名内部类是一种特殊的内部类,它没有具体的类名。这种类通常用于一次性使用的场景,如实现接口或继承某个类时。由于匿名内部类没有名称,因此在定义的同时会立即创建一个对象实例。这种类的主要优点是语法简洁,使代码更加清晰和简洁,尤其适用于事件处理(例如GUI编程中的事件监听器)和在方法调用中实现接口的情况。

匿名内部类可以访问外部类的所有成员,包括私有成员,以及定义它的方法内的final或effectively final局部变量。然而,匿名内部类不能定义静态成员、方法或类,并且由于缺乏具体的类名,它们也不能有构造方法。

public class Main {
  
    public static void main(String[] args) {
        // 使用匿名内部类实现Runnable接口
        Runnable r = new Runnable() {
            public void run() {
                System.out.println("匿名内部类的run方法");
            }
        };

        new Thread(r).start();
    }
    

}

5、闭包和回调

闭包是一种编程概念,指的是一个函数及其引用的外部环境的组合。在Java中,闭包通常通过内部类实现。由于内部类可以访问其外部类的成员变量和方法,这使得它可以捕获并持有这些外部变量的状态,从而形成闭包。

public class Main {
  
    public static void main(String[] args) {
        new Thread(new Main().createClosure()).start();
    }
    private int outerField = 100;

    public Runnable createClosure() {
        class Inner implements Runnable {
            public void run() {
                System.out.println(outerField);
            }
        }
        return new Inner();
    }
    

}

回调是一种常见的编程模式,用于异步或事件驱动编程。回调通常是作为一个函数传递到另一个函数中,当特定事件或条件发生时,这个传递的函数被调用。在Java中,回调经常通过使用接口或抽象类以及内部类来实现。

public class Main {
  
    public static void main(String[] args) {
        EventHandler eventHandler = new EventHandler(new Callback() {
            @Override
            public void onEvent() {
                System.out.println("Callback triggered!");
            }
        });

        eventHandler.doSomething();
    }
  
}
interface Callback {
    void onEvent();
}

class EventHandler {
    private Callback callback;

    public EventHandler(Callback callback) {
        this.callback = callback;
    }

    public void doSomething() {
        // 事件处理...
        callback.onEvent(); // 触发回调
    }
}

推荐文档