一、内部类

  1、引入

    类的成员包括:

    (1)属性:成员变量;

    (2)方法:成员方法;

    (3)构造器;

    (4)代码块;

    (5)内部类;

    其中1、2是代表这类事物的特征;

    其中3、4是初始化类和对象用的;

    其中5是协助完成2的功能的实现,表现;

  2、内部类

    当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使内部类
    在Java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类
    Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称。
    Inner class的名字不能与包含它的外部类类名相同;

  3、分类

    根据内部类的所在的位置不同:

    (1)成员内部类static成员内部类和非static成员内部类)外部类中方法外;

    (2)局部内部类:匿名内部类,方法体内(也可以在代码块内)

  4、

二、成员内部类

  1、概述及分类

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

    (1)有 static 修饰的:静态成员内部类,简称 静态内部类;

    (2)没有 static 修饰的:非静态成员内部类,简称 成员内部类

      思考:什么情况下会用到成员内部类(方法外声明的?)

当描述一个事物时,发现它的内部还有一个完整的结构需要用一个类来描述;
并且发现这内部的结构,如果独立存在是没有意义的,必须在这个外部类中才有意义。而且这个内部结构只为这个外部类服务。

例如:Body身体,发现它内部还有完整的结果,如:心脏,发现心脏单独创建对象没有意义,只有在 Body 对象中才有意义,而且只为 Body 对象服务。

   2、成员内部类的特点

    (1)成员内部类作为类的成员的角色

      ① 和外部类不同,Inner class 还可以声明为 private protected

      ② 可以调用外部类的结构;

      ③ Inner class 可以声明为 static 的,但此时就不能再使用外层类的非static 的成员变量;

    (2)成员内部类作为类的角色

      ① 成员内部类,可以出现原本类中能够定义的所有的成员:

            属性:可以有静态属性和非静态属性;

          方法:可以有静态方法和非静态方法,如果静态内部类是抽象类,还可定义抽象方法;

          构造方法:有参方法、无参方法;

          代码块:可以有静态代码块和非静态代码块;

          内部类:允许,但很少再写内部类(臃肿)

      ② 可以声明为 abstract 类,因此可以被其他的内部类继承;

      ③ 可以声明为 final 类;

      ④ 编译以后生成 OuterClass$InnerClass.class 字节码文件(也适用于局部内部类

    (3)注意

      ① 非 static 的成员内部类中的成员不能声明为 static 的,只有在外部类或 static 的成员内部类才可声明 static 成员;

      ② 外部类访问成员内部类的成员,需要“内部类.成员” 或 “内部类对象.成员” 的方式;

      ③ 成员内部类可以直接使用外部类的所有成员,包括私有的数据;

      ④ 当想要在外部类的静态成员部分使用内部类时, 可以考虑内部类声明为静态的;

  2、静态成员内部类(静态内部类)

    (1)语法格式

【修饰符】 class 外部类  【 extends 父类】 【implements 父接口们】{
  【其他修饰符】 static class 内部类  【 extends 父类】 【implements 父接口们】{
   }
 }

       注意

        ① 只有成员内部类才能用 static 修饰,其他的外部类,局部内部类等够不可以用 static 修饰。

        ② 外部类,内部类的父类,父接口都没有关系,各是各自的。

 

    (2)特点

      ① 静态内部类中不能使用外部类的非静态的成员;

      ② 在外部类中,使用静态内部类,和使用其他的类一样的原则;

        如果使用静态内部类的静态成员,直接“静态内部类名.”

        如果使用静态内部类的非静态成员,直接“静态内部类对象名.

      ③ 在外部类的外面,使用静态内部类

        如果使用静态内部类的静态成员,直接“类名”;

          使用 外部类名.静态内部类名.静态方法

          使用 import 包.外部类名.静态内部类名; 然后在代码中使用“静态内部类名

      ④ 静态内部类不会随着外部类的初始化一起初始化,而是要在使用到这个静态内部类才会初始化;

    (3)小节

      ① 同级的来说静态的不能直接使用非静态的

      ② 访问一个类的静态成员,用 “类名.” 即可访问一个类的非静态成员,用“对象名.”即可

      ③ 当使用到这个类时,这个类才会进行初始化

    (4)Demo:

 

 1 package com.java.test;
 2 //import com.java.test.Outer.Inner;
 3 
 4 public class TestStaticInner {
 5     public static void main(String[] args) {
 6        // 访问内部类的静态方法
 7                // Inner.test();//上面有导包语句,import 包.外部类名.静态内部类名;
 8         
 9         Outer.Inner.test();//外部类名.静态内部类名.静态方法(不使用导包语句)
10         
11        // 访问内部类的非静态方法
12                 //  Inner in = new Inner();//上面有导包语句,import 包.外部类名.静态内部类名;
13         Outer.Inner in = new Outer.Inner();
14         in.method();
15         
16         Outer out = new Outer();  // 内部类不会初始化
17         out.outMethod();              // 该方法中用到内部类,内部类会进行初始化
18     }
19 }
20 class Outer{
21     private int i = 1;
22     private static int j = 2;
23     
24     static{
25         System.out.println("外部类的静态代码块");
26     }
27     
28     static class Inner{
29         static{
30             System.out.println("静态内部类的代码块");
31         }
32         
33         public void method(){
34             System.out.println("静态内部类的非静态方法");
35             //System.out.println(i);//错误,不能访问非静态的
36             System.out.println(j);
37         }
38         
39         public static void test(){
40             System.out.println("静态内部类的静态方法");
41         }
42     }
43     // 外部类访问内部类
44     public void outMethod(){
45         Inner in = new Inner();
46         in.method();//非静态方法,用对象名.访问
47         
48         Inner.test();//静态方法,用类名.访问
49     }
50 }

 

    (5)

  3、成员内部类(非静态成员内部类)

    (1)语法格式

【修饰符】 class 外部类 【 extends 父类】 【implements 父接口们】{
  【其他修饰符】  class 内部类  【 extends 父类】 【implements 父接口们】{
   }
 }

 

    (2)特点

      ① 在非静态内部类中,不能出现任何和 static 有关的声明;

      ② 在非静态内部类中,可以随意访问外部类的所有的成员,包括静态的和非静态的;    

      ③ 在外部类的静态成员中,不能使用非静态的成员内部类;

      ④ 在外部类的外面使用

        第一步:先创建外部类的对象

        第二步:要么通过外部类的对象,去创建内部类的对象

Outer out = new Outer();
Outer.Inner in = out.new Inner();

             要么通过外部类的对象,去获取内部类的对象

Outer out = new Outer();
Outer.Inner in  = out.getInner();

  

    (3)Demo

 1 public class TestNonStaticInner {
 2     public static void main(String[] args) {
 3         //Outer.Inner in = new Outer.Inner();//错误的
 4         
 5         //在这里使用Inner,因为此时的Inner是Outer的非静态成员,所以需要用到Outer的对象
 6         Outer out = new Outer();
 7         //Outer.Inner in = out.new Inner();
 8         Outer.Inner in  = out.getInner();
 9         in.method();
10     }
11 }
12 class Outer{
13     private int i = 1;
14     private static int j = 2;
15     
16     class Inner{
17         public void method(){
18             System.out.println("非静态内部类的非静态方法");
19             System.out.println(i);
20             System.out.println(j);
21         }
22     }
23     
24     public static void outTest(){
25         //Inner in = new Inner();//静态的方法不能访问非静态的成员
26     }
27     public void outMethod(){
28         Inner in = new Inner();
29         in.method();
30     }
31     
32     public Inner getInner(){
33         return new Inner();
34     }
35 }

 

    (4)练习案例:创建一个外部类,里面有一个抽象内部类,并有一个抽象方法,要求写一个类去继承该内部类;

       代码:

 1 public class Test {
 2     public static void main(String[] args) {
 3         MySub my = new MySub(new Outer());
 4         my.test();
 5     }
 6 }
 7 class Outer{
 8     abstract class Inner{
 9         public abstract void test();
10     }
11 }
12 class MySub extends Outer.Inner{
13     MySub(Outer out){
14         out.super();//需要外部类的对象,才能调用非静态内部类的构造器
15     }
16 
17     @Override
18     public void test() {
19         System.out.println("hello");
20     }
21 }

   (5)小节

      ① 使用非静态内部类名时,可以使用:import 包.外部类名.内部类名; 或 外部类名.内部类名;

      ② 要调用非静态内部类的构造器,需要用到外部类的对象;

      ③ 因为子类的构造器的首行一定要调用父类的构造器,默认调用父类的无参构造(需要借助外部类对象);

      ④ 继承抽象内部类,要重写抽象类的抽象方法;

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

 

三、局部内部类

  1、概述

    局部内部类:定义在类中方法中(方法内、代码块内、构造器内)的类,就是局部内部类

          即只有当前所属的方法才能使用它,出了这个方法外面就不能使用了。

  2、分类

    (1)有名字的局部内部类:简称 局部内部类;

    (2)没名字的局部内部类:简称 匿名内部类。

  3、语法格式

    局部内部类定义格式

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

    说明:如果子类调用的是父类的无参构造,那么()中实参列表不用写;

        如果子类调用的是父类的有参构造,那么()中传入实参列表;

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

        (1)外部类:public / default

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

        (3)局部内部类:不加任何修饰符

 

  4、特点

    (1)局部内部类的修饰符,只能有 abstract 或 final;

    (2)局部内部类有使用的作用域;

    (3)如果局部内部类在静态方法中,不能使用外部类的非静态成员;

    (4)在局部内部类中,可以使用当前局部内部类所在方法的局部变量,但是要求,这个局部变量必须是 final的常量

 

  5、扩展

    在Java8时,如果某个局部变量被局部内部类使用了,会自动添加final变为常量,一旦变为常量,它的值就不能修改了。

   为什么它要这么要求?给局部变量加 final呢?

   避免局部内部类对象被返回到外部类的外面使用时,访问不到这个局部变量,所以要把这个局部变量变为final的常量。(常量在方法区中)

   原因:

        ① new出来的对象在堆内存当中。

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

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

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

    Demo:

 1 public class TestLocalInner {
 2     public static void main(String[] args) {
 3         Outer out = new Outer();
 4         Father in = out.test();//在外部类的外面虽然不能使用局部内部类,但是可以得到它的对象
 5         System.out.println(in.getClass());
 6         in.method();//在这里仍然可以访问到这个a,那么这个a就不能存在栈中,得挪到方法区,变为常量
 7     }
 8 }
 9 abstract class Father{
10     public abstract void method();
11 }
12 class Outer{
13     private int i = 1;//成员变量,实例变量,非静态成员变量
14     private static int j = 2;//成员变量,类变量,静态变量
15     
16     public Father test(){
17         //Inner in = new Inner();   现在还没有声明该类,不能使用
18         
19         final int a = 10;//局部变量==>局部的常量
20         
21         //局部内部类
22         class Inner extends Father{
23             public void method(){
24                 System.out.println(i);
25                 System.out.println(j);
26                 System.out.println(a);
27             }
28         }
29         
30         Inner in = new Inner();
31         in.method();
32         
33         return in;
34     }
35     
36     public void method(){
37         //Inner in = new Inner();  超过了作用域
38     }
39     
40     public static void fun(){
41         //局部内部类
42         class Inner{
43             public void method(){
44                 //System.out.println(i);//是因为fun方法是静态的
45                 System.out.println(j);
46             }
47         }
48     }
49 }

 

  6、

四、匿名内部类【重要】

  1、概述

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

    匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。一个匿名内部类一定是在 new 的后面,用其隐含实现一个接口或实现一个类。

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

    (1)定义子类

    (2)重写接口中的方法

    (3)创建子类对象

    (4)调用重写后的方法

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

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

    

  2、语法格式

    定义格式:

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

     特殊:声明匿名内部类与创建它的对象是一起完成的,即匿名内部类只有唯一的对象。

    匿名内部类的特点:

    (1)匿名内部类必须继承父类或实现接口;

    (2)匿名内部类只能有一个对象;

    (3)匿名内部类对象只能使用多态形式引用;

  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 }

  4、

五、

posted on 2020-12-06 22:40  格物致知_Tony  阅读(195)  评论(0编辑  收藏  举报