如果一个事物的内部包含另一个事物,那么这就是一个类内部包含另一个类。

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)
成员内部类

定义格式:

修饰符 class 外部类名称 {
    修饰符 class 内部类名称 {
        // ....
    }
    // ....
}

内部类的class文件命名为:外部类$内部类.class

使用成员内部类

  1. 间接方式: 在外部类的方法当中,使用内部类;然后main方法调用外部类的方法。
  2. 直接方式:
    格式:
外部类名称 内部类名称 对象名 = new 外部类名称().new 内部类名称()

内部类的同名变量访问

如果内部类和外部类出现了重名现象,那么访问格式为:

// 类名称.this.类成员变量名;
public class Outer {
    private int num = 10;
    
    public class Inner {
        
        int num = 20;
        
        public void method {
            
            int num = 30;
            System.out.println(num); // 30
            System.out.println(this.num); // 20
            System.out.println(Outer.this.num); // 10
        }

}
局部内部类定义

如果一个类的成员方法里面定义了一个类,那么里面的类就是局部内部类。

定义格式:

修饰符 class 外部类名称 {
    修饰符 返回值类型 外部类方法名称(参数列表) {
        class 局部内部类名称 {
            // ...
        }
    }
}

注意: 定义一个类的时候,权限修饰符规则:

  • 外部类:public/ (default)
  • 成员内部类:public/ protected/ (default)/ private
  • 局部内部类:什么都不能写

局部内部类的final问题

局部内部类,如望访问所在方法的局部变量,那么这个局部变量必须是【有效final的】。

备注: 从Java8+开始,只要局部变量事实不变,那么 final关键字可以省略。

原因:

  1. new出来的对象在堆内存当中。
  2. 局部变量是跟着方法走的,在栈内存当中
  3. 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
  4. 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
  5. 因此如果对象想调用已经消失的局部变量就必须将值复制到方法的变量中,不可更改。
* 匿名内部类

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

匿名内部类的定义格式:

接口名称 对象名 = new 接口名称() {
    // 覆盖重写所有抽象方法
}; // 注意末尾的分号不能省略

好处:不必再为了实现一个接口而单独定义一个类。

匿名内部类的注意事项

对格式new 接口名称(){...}进行解析:

  1. new代表创建对象的动作
  2. 接口名称就是匿名内部类需要实现哪个接口
  3. {...}这才是匿名内部类的内容

另外还要注意几点问题:

  1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    如果希望多次创建对象,而且类的内容一样的话,那么就必须使用单独定义的实现类。
  2. 匿名对象,在【调用方法】的时候,只能调用唯一一次
    如果希望同一个对象,调用多次方法,那么必须给对象起个名字。
  3. 匿名内部类是省略了【实现类/子类名称】,匿名对家是省略了【对象名称】
// 定义一个匿名内部类+匿名对象
new InterfaceImpl {
    @Override
    public void method {
        System.out.println("定义了一个匿名内部类+匿名对象")
    }
}.method;

// 输出为:定义了一个匿名内部类+匿名对象