浅谈Java中的四种内部类

概述

  在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

  广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、静态内部类和匿名内部类。

成员内部类

  从名字就可以看出,这种内部类是作为类的成员而存在的,其定义位于类的内部。

//外部类Outer
public class Outer {
    //外部类成员属性
    private int a = 3;
    //创建一个内部类的实例
    private Inner in;
    public Outer(){
        in = new Inner();
    }
    public int getInnerA(){
        // 引用内部类的变量,需通过实例
        return in.a;
    }
    //成员内部类Inner
    public class Inner {
        //内部类成员属性
        public int a = 2;
        //内部类成员方法
        public void doSomething() {
            // 调用外部类的方法或属性:外部类名.this.成员方法/属性。
            System.out.println(Outer.this.a);//3
            System.out.println(a);//2
        }
    }
}

//与外部类Outer平级的类Extender继承内部类Inner
class Extender extends Outer.Inner{
    //构造方法中需要传入父类的实例对象
    public Extender(Outer outer){
        //外部类实例名.super(内部类参数列表)
        outer.super();
    }
}
public class Test {
    public static void main(String[] args) {
        Outer outer=new Outer();
        System.out.println(outer.getInnerA());//2

        //外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数)
        //Outer.Inner inner = outer.new Inner();
        //外部类名.内部类名 实例名 = new 外部类构造方法(参数).new 内部类构造方法(参数)
        Outer.Inner inner = new Outer().new Inner();
        inner.doSomething();

        Extender extender=new Extender(outer);
        extender.doSomething();

    }
}

注意事项  

  • 外部类无法直接访问成员内部类的方法和属性,需要通过内部类的一个实例来访问

     成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员),访问外部类的成员方法和属性时使用:外部类名.this.成员方法/属性。

  • 创建成员内部类的实例使用:

   直接方法:

    1. 外部类名.内部类名 实例名 = new 外部类构造方法(参数).new 内部类构造方法(参数)
    2. 外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数)
      前提是已经创建了一个外部类的对象,可以理解为隐式地保存了一个引用,指向创建它的外部类对象

   间接方法:

    在外部类的方法中使用内部类,在主函数中只是调用外部类的方法。

  • 成员内部类中不能存在static关键字,即,不能声明静态属性、静态方法、静态代码块等。
  • 非静态内部类也可以定义静态成员但需要同时有final关键词修饰,静态方法鉴于无法用final修饰,仍必须是在静态内部类或者非内部类中定义。
  • 与外部类平级的类继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”。
  • 内部类在编译之后生成一个单独的class文件,里面包含该类的定义,所以内部类中定义的方法和变量可以跟父类的方法和变量相同。
    例如上面定义的四个类的class文件分别是:Outer.class、Outer$Inner.class、Extender.class和Test.class四个文件。

局部内部类

  定义在代码块、方法体内、作用域(使用花括号“{}”括起来的一段代码)内的类叫局部内部类。

public class Outer {
    public void methodOuter(){
        final int num=20;
        class Inner{//局部内部类
            public void methodInner(){
                System.out.println(num);//20
            }
        }
        Inner inner=new Inner();
        inner.methodInner();
    }
}
public class Test {
    public static void main(String[] args) {
        Outer obj=new Outer();
        obj.methodOuter();
    }
}

注意事项

  1. 局部内部类只能在代码代码块、方法体内和作用域中使用(如创建对象和使用类对象等)
  2. 局部内部类访问作用域内的局部变量,该局部变量需要使用final修饰。
    注意:该局部变量也可以不用final修饰(从Java8+开始),是有效final的也行(就是只赋给局部变量一次值并且保证不变即可)
  3. 可以使用abstract修饰,声明为抽象类。

补充

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

    1. 外部类:public、(default)
    2. 成员内部类:public、protected、(default)、private
    3. 局部内部类:(default)

静态内部类

  使用static修饰的成员内部类叫静态内部类。

与成员内部类的对比如下:

说明

成员内部类

静态内部类

静态成员

静态成员需同时有final关键词修饰

可以

静态方法

不可定义

可以

访问外部类非static属性/方法

外部类名.this.成员方法/属性

不可以

外部类访问内部类

需要通过内部类的一个实例来访问

需要通过内部类的一个实例来访问

创建实例

外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数)

外部类名.内部类名 实例名 = new 外部类名.内部类名(参数)

编译后的class文件

单独的class文件(内部类中的方法和变量可以跟父类的方法和变量同名),外部类$内部类.class

单独的class文件(内部类中的方法和变量可以跟父类的方法和变量同名),外部类$内部类.class

其他

与外部类平级的类继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”

 

匿名内部类

匿名内部类的定义和实例化形式如下:

new 父类构造方法(参数){ 
         //注:该方法名必须在父类中已经存在 
     修饰符 返回参数类型 方法名(参数列表){ 
          //...
     } 
}

注意事项

  1. 只能使用一次,创建实例之后,类定义会立即消失。
  2. 必须继承一个类(抽象的、非抽象的都可以)或者实现一个接口。如果父类(或者父接口)是抽象类,则匿名内部类必须实现其所有抽象方法。
  3. 不能是抽象类,因为匿名内部类在定义之后,会立即创建一个实例。
  4. 不能定义构造方法,匿名内部类没有类名,无法定义构造方法,但是,匿名内部类拥有与父类相同的所有构造方法。
  5. 可以定义代码块,用于实例的初始化,但是不能定义静态代码块。
  6. 可以定义新的方法和属性(不能使用static修饰),但是无法显式的通过“实例名.方法名(参数)”的形式调用,因为使用new创建的是“上转型对象”(即父类声明指向子类对象)。
  7. 匿名内部类,在创建对象的时候,只能使用唯一一次。

    如果希望多次创建对象,而且类的内容一样的话,那么就要使用单独定义的实现类了。

   ②匿名对象,在调用方法的时候,只能调用唯一一次。

    如果希望同一个对象,调用多次方法,那么必须给对象起个名字。

   ③匿名内部类是省略了实现类/子类名称,但是匿名对象是省略了对象名称匿名内部类和匿名对象不是一回事)。

说明

成员内部类

匿名内部类

静态成员

静态成员需同时有final关键词修饰

不可定义

静态方法

不可定义

不可定义

访问外部类非static属性/方法

外部类名.this.成员方法/属性

外部类名.this.成员方法/属性

外部类访问内部类

需要通过内部类的一个实例来访问

需要通过内部类的一个实例来访问

创建实例

外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数)

如上:父类 实例名 = new 父类(){}

编译后的class文件

单独的class文件(内部类中的方法和变量可以跟父类的方法和变量同名),外部类$内部类.class

单独的class文件,使用类$数字.class

其他

与外部类平级的类继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”

 

posted @ 2020-10-27 15:35  A_xiba  阅读(240)  评论(0编辑  收藏  举报