使用内部类原因:

  • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
  • 内部类可以对同一个包中的其他类隐藏起来。
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。

# 使用内部类访问对象状态

package com.example.demo.test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
public class InnerClassTest {
    public static void main(String[] args) {
        Person person = new Person(true);
        person.run();
    }
}
class Person {
    private boolean isHungry;
    public Person(boolean isHungry) {
        this.isHungry = isHungry;
    }
    public void run() {
        Eat eat = new Eat();
        eat.eatSomthing();
    }
    public class Eat {
        public void eatSomthing() {
            if (isHungry) {
                System.out.println("Eating!");
            }
        }
    }
}

运行结果:

Eating!

在内部类 Eat 中,可以访问到类外部的 isHungary 变量。

内部类对象拥有一个对外围类对象的引用,如下图:

这个引用 outer 类似于 super,但是 outer 并不是 Java 的关键字。

Eat 内部类中,没有定义构造器,编译器为其生成了一个默认的构造器,代码如下:

public Eat(Person person) {
	outer = person;
}

Person 类中 run 方法 new 一个 Eat 对象时,默认传入一个 this 引用参数,代码如下:

Eat eat = nwe Eat(this);

# 内部类的特殊语法规则

表达式 OuterClass.this 表示外围类的引用。例如,在 Eat 内部类中:

if (Person.this.isHungry) {
    System.out.println("Eating!");
}

表达式 outerObject.new InnerClass(construction parameters) 表示内部对象的构造器。例如,在 Person 类的 run 方法中:

Eat eat = this.new Eat();

当使用另一个外部对象 new 一个内部对象时,这种写法就比较有意义。

package com.example.demo.test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
public class InnerClassTest {
    public static void main(String[] args) {
        Person person = new Person(false);
        person.run();
    }
}
class Person {
    private boolean isHungry;
    public Person(boolean isHungry) {
        this.isHungry = isHungry;
    }
    public void run() {
        Person person = new Person(true);
        Eat eat = person.new Eat();
        eat.eatSomthing();
    }
    public class Eat {
        public void eatSomthing() {
            if (Person.this.isHungry) {
                System.out.println("Eating!");
            }
        }
    }
}

运行结果:

Eating!

创建了一个 isHungary 为 false 的 person 对象,如果创建这个对象的内部对象,程序运行不会打印出任何结果。在 run 方法中,创建的 isHungary 为 true 的 person 的内部对象,所以程序运行有输出结果。

表达式 OuterClass.InnerClass 表示在外围类中引用内部类。也可以在 Person 类外部实现 run 方法的逻辑:

public class InnerClassTest {
    public static void main(String[] args) {
        Person person = new Person(true);
        Person.Eat eat = person.new Eat();
        eat.eatSomthing();
    }
}

运行结果:

Eating!

# 局部内部类

Eat 类只有在 run 方法中使用过一次,像这种只使用一次的内部类,可以在方法中定义一个局部内部类。

public void run() {
    class Eat {
        public void eatSomthing() {
            if (isHungry) {
                System.out.println("Eating!");
            }
        }
    }
    Eat eat = new Eat();
    eat.eatSomthing();
}

局部内部类的作用域声明这个局部类的块中,不能用 public 或 private 访问修饰符进行声明。

# 匿名内部类

如果只想要创建一个对象,并不关系这个类是什么,可以使用匿名内部类。

package com.example.demo.test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
public class InnerClassTest {
    public static void main(String[] args) {
        Person person = new Person(true);
        person.run();
    }
}
class Person {
    private boolean isHungry;
    public Person(boolean isHungry) {
        this.isHungry = isHungry;
    }
    public void run() {
        Man man = new Man() {
            @Override
            public void eat() {
                if (isHungry) {
                    System.out.println("Eating!");
                }
            }
        };
        man.eat();
    }
}
interface Man {
    void eat();
}

运行结果:

Eating!

# 静态内部类

当不需要内部类引用外部类对象时,可以将内部类声明为 static,即静态内部类。

package com.example.demo.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class InnerClassTest {
    public static void main(String[] args) {
        Person.Number number = Person.run(new ArrayList<Integer>()