一、概述

  内部类:将一个类 A 定义在另一个类 B 里面,里面的那个类 A 就称为 内部类,类 B 则称为 外部类

二、成员内部类

  1、概述

    成员内部类:定义在 类中方法外 的类。

  2、定义格式

     定义格式

class 外部类 {
    class 内部类{

    }
}

  3、访问

  •  内部类可以直接访问外部类的成员,包括私有成员。
  •     外部类要访问内部类的成员,必须要建立内部类的对象。

     创建内部类对象格式:

外部类名.内部类名 对象名 = new 外部类型().new 内部类型();

Demo:

1 public class Person {
 2     private boolean live = true;
 3     // 成员内部类
 4     class Heart {
 5         public void jump() {
 6             // 直接访问外部类成员
 7             if (live) {
 8                 System.out.println("心脏在跳动");
 9             } else {
10                 System.out.println("心脏不跳了");
11             }
12         }
13     }
14     public boolean isLive() {
15         return live;
16     } 
17     public void setLive(boolean live) {
18         this.live = live;
19     }
20 }        
21 
22 // 测试类
23 public static void main(String[] args) {
24     // 创建外部类对象
25     Person p = new Person();
26     // 创建内部类对象,调用内部类方法方式一
27     Heart heart = p.new Heart();
28     // 调用内部类方法
29     heart.jump();
30     // 调用外部类方法
31     p.setLive(false);
32     // 调用内部类方法
33     heart.jump();
34 
35     // 调用内部类方法方式二
36     Person.Heart heart = new Person().new Hear();
37     heart.jump();
38     }
39 }
40 
41 运行结果:
42 心脏在跳动
43 心脏不跳了

注意:内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和 $ 符号。如:Person$Heart.class

三、局部内部类

  1、概述

    局部内部类:定义在 类中方法内 的类,就是局部内部类。即只有当前所属的方法才能使用它,出了这个方法外面就不能使用。

  2、定义格式

    定义格式

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

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

    (1)外部类: public / (default)

    (2)成员内部类:public / protected / (default) / private 

    (3)局部内部类:什么都不能写。

  3、扩展

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

   原因:

    (1)new出来的对象在堆内存当中。

    (2)局部变量是跟着方法走的,在栈内存当中。

    (3)方法运行结束之后,立刻出栈,局部变量就会立刻消失。

    (4)但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。

四、匿名内部类【重要】

  1、概述

    匿名内部类:是内部类的简化写法。它的本质是一个 带具体实现的  父类或者父接口的 匿名的 子类对象

    开发中,最常用到的内部类就是匿名内部类了,以接口为例,当你使用一个接口时,都得做如下几步操作:

    (1)定义子类

    (2)重写接口中的方法

    (3)创建子类对象

    (4)调用重写后的方法

    而匿名类就可以把以上四步合成一步。

    前提:匿名内部类必须继承一个父类或者实现一个父接口

  2、格式

定义格式

new 父类名或者接口名(){
    // 方法重写
    @Override
    public void method() {
        // 执行语句
    }
};

  3、使用方式

以接口为例,匿名内部类的使用。

   定义接口:

1 public abstract class FlyAble{
2     public abstract void fly();
3 }

创建匿名内部类,并调用:

1 public class InnerDemo {
 2     public static void main(String[] args) {
 3         /*
 4         1.等号右边:是匿名内部类,定义并创建该接口的子类对象
 5         2.等号左边:是多态赋值,接口类型引用指向子类对象
 6        */
 7         FlyAble f = new FlyAble(){
 8           public void fly() {
 9               System.out.println("我飞了~~~");
10           }
11         };
12         //调用 fly方法,执行重写后的方法
13         f.fly();
14     }
15 }

  通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。如下所示

1 public class InnerDemo2 {
 2     public static void main(String[] args) {
 3         /*
 4         1.等号右边:定义并创建该接口的子类对象
 5         2.等号左边:是多态,接口类型引用指向子类对象
 6         */
 7         FlyAble f = new FlyAble(){
 8           public void fly() {
 9               System.out.println("我飞了~~~");
10           }
11     };
12     // 将f传递给showFly方法中
13         showFly(f);
14     } 
15     public static void showFly(FlyAble f) {
16         f.fly();
17     }
18 }

  将以上两步,也可以简化为一步:

1 public class InnerDemo3 {
 2     public static void main(String[] args) {
 3     /*
 4         创建匿名内部类,直接传递给showFly(FlyAble f)
 5     */
 6     showFly( new FlyAble(){
 7          public void fly() {
 8             System.out.println("我飞了~~~");
 9         }
10     });
11 } 
12     public static void showFly(FlyAble f) {
13         f.fly();
14     }
15 }