实例内部类 静态内部类 局部内部类
主要特征 内部类的实例引用特定的外部类的实例 内部类的实例不与外部类的任何实例关联 可见范围是所在的方法
可用的修饰符 访问控制修饰符,abstract, final 访问控制修饰符,static, abstract, final abstract, final
可以访问外部类的哪些成员 可以直接访问外部类的所有成员 只能直接访问外部类的静态成员 可以直接访问外部类的所有成员,并且只能访问所在方法的 final 类型的变量和参数
拥有成员类型 只能拥有实例成员 可以拥有静态成员和实例成员 只能拥有实例成员
外部类如何访问内部类的成员 必须通过内部类的实例来访问 对于静态成员,可以通过内部类的完整类名来访问 必须通过内部类的实例来访问
  • 内部类:定义在类结构中的另一个类,编译后,每个内部类都会生成对应的 .class 文件

  • 四种内部类:

    1. 实例内部类:没有使用 static 修饰的内部类,OuterClass$InnerClass. class
    2. 静态内部类:使用 static 修饰的内部类,OuterClass$InnerClass. class
    3. 局部内部类:在方法中定义的内部类,OuterClass$NInnerClass. class(增加了一个数字 N,用于区分在不同方法中定义的同名的局部内部类)
    4. 匿名内部类:没有名称的局部内部类,适合只需要使用一次的类,OuterClass$N
  • 特点:

    1. 实例内部类不能含有 static 的变量和方法
    2. 静态内部类(嵌套类)不能访问外部类的非 static 成员
    3. 局部内部类及匿名内部类对象不能使用该内部类所在方法的非 final 局部变量

# 匿名内部类

  • 在定义匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用
  • 匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口
  • 匿名内部类不能定义构造器,不能是抽象类
  • 被匿名内部类访问的局部变量必须使用 final 修饰(从 Java 8 开始,被匿名内部类访问的局部变量自动使用了 final 修饰)

定义格式

// 下面代码其实是创建了一个对象
new 实现接口() 或 父类构造器([实参列表]) {
    // 匿名内部类的类体部分
}
1
2
3
4
// 定义匿名内部类继承 Animal,并创建这个类匿名对象
new Animal() {
};

// 定义匿名内部类继承 Bird,并创建这个类匿名对象,之后调用 fly 方法
new Bird() {
    public void fly() {
        System.out.println("飞翔");
    }
}.fly();

// 定义匿名内部类实现 IWalkable,并创建这个类匿名对象,之后调用 walk 方法
new IWalkable() {
    @Override
    public void walk() {
        System.out.println("走路");
    }
}.walk();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 外部类
class Outer {
    String str1 = "Outer 实例变量";
    static String str2 = "Outer 类变量";

    // 实例内部类
    class Inner1 {
        String str1 = "Inner1 实例变量";
        final static String str2 = "Inner1 类变量"; // 实例内部类中不能定义类成员,除非使用 final 修饰
    
        public void doWork() {
            String str1 = "Inner1 局部变量";
            System.out.println(str1);            // Inner1 局部变量
            System.out.println(this.str1);       // Inner1 实例变量
            System.out.println(Outer.this.str1); // Outer 实例变量
        }
    }

    // 静态内部类
    static class Inner2 {
        String str1 = "Inner2 实例变量";
        static String str2 = "Inner2 类变量";        

        public void doWork() {
            String str1 = "Inner2 局部变量";
            System.out.println(str1);       // Inner2 局部变量
            System.out.println(this.str1);  // Inner2 实例变量
            // System.out.println(Outer.this.str1); // 无法访问外部类的非静态成员
            System.out.println(Outer.str2); // Outer 类变量
        }
    }

    public void doWork() {
        final String STR3 = "方法中局部变量";
        
        // 局部内部类
        class Inner3 {
            String str1 = "Inner3 实例变量";
            // static String str2 = "Inner3 类变量"; // 局部内部类中不能定义类成员
            
            public void innerDoWork() {
                String str1 = "Inner3 局部变量";
                System.out.println(str1); // Inner3 局部变量
                System.out.println(this.str1); // Inner3 实例变量
                System.out.println(STR3); // 被局部内部类访问的局部变量必须使用final修饰
                // System.out.println(Outer.this.str1); // 无法访问外部类的非静态成员
                System.out.println(Outer.str2); // Outer 类变量
            }
        }

        new Inner3().innerDoWork(); // 只能在局部内部类所在的方法中创建对象
    }
}

// 测试类
class InnerClassDemo {
    public static void main(String[] args) {
        // 通过外部类对象创建实例内部类对象
        Outer.Inner1 in1 = new Outer().new Inner1();
        in1.doWork();

        // 创建一个静态内部类对象不需要外部类对象
        // 直接调用静态内部类的构造器创建对象,需用完整的类名
        Outer.Inner2 in2 = new Outer.Inner2(); 
        in2.doWork();

        new Outer().doWork();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
Updated at: 2020-08-09 01:36:43