什么是匿名内部类,它有什么特征

围巾🧣 2020年06月04日 389次浏览

匿名内部类使用注意事项

  • 匿名内部类也就是没有名字的内部类
  • 正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编
  • 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
  • 匿名内部类中是不能定义构造函数的

使用的形参为什么要final

  1. 我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final
  2. 为什么必须要为final呢?
    • 首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用
    • 在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
    • 简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
    • 故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。

匿名内部类初始化

我们一般都是利用构造器来完成某个实例的初始化工作的,但是匿名内部类是没有构造器的!那怎么来初始化匿名内部类呢?使用构造代码块!利用构造代码块能够达到为匿名内部类创建一个构造器的效果

常见的匿名内部类实例

public class Main {

    public static void main(String[] args) {

        Runnable r = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(i + "");
            }
        };
        Thread thread = new Thread(r);
        thread.start();
    }
}

Java内部类小结

  1. 成员内部类

    也是最常规的内部类。该内部类相当于位于外部类的成员的位置。该内部类可以访问外部类的所有成员和方法,即使是private.

    public class Main {
    
        public static void main(String[] args) {
            Outer outer = new Outer();
            Outer.Inner inner = outer.new Inner();
            inner.print("Inner 1");
    
            inner = outer.getInner();
            inner.print("Inner 2");
        }
    }
    
    class Outer {
        public class Inner {
    
            public void print(String string) {
                System.out.println(string);
            }
        }
    
        public Inner getInner() {
            return new Inner();
        }
    }
    
  2. 静态内部类

    1. 该类被修饰为static。同时作为静态内部类,他不能访问外部类非静态的成员或方法。
    2. 访问方式如上所述。
    3. 一般书写静态类时,不涉及成员变量。
    public class Main {
    
        public static void main(String[] args) {
            Outer.Inner inner = new Outer.Inner();
            inner.print("emmmm");
        }
    }
    
    class Outer {
        static class Inner {
    
            public void print(String string) {
                System.out.println(string);
            }
        }
    
        public Inner getInner() {
            return new Inner();
        }
    }
    
  3. 方法内部类

    类被创建在外部类方法中

    1. 可以访问外部类中的成员
    2. 不能被static private修饰,因为他不再是成员位置,只有成员才能被修饰为static..…
    3. 因此内部类不能有静态成员
    4. 内部类要访问外部方法中的局部变量时,该局部变量需要被修饰成final。
    class People {
        public People() {
        }
    }
    
    class Man{
        public Man() {
        }
    
        public People getWoman() {
            class Woman extends People { // 局部内部类
                int age = 18;
            }
            return new Woman();
        }
    }
    
  4. 匿名内部类

    1. 就是一个内部类的简化版
    2. 定义匿名内部类的前提是,该类必须继承一个外部类或者实现接口。
      具体实现如下图
      这是一个继承类的正常版,一共分为这几步

为什么要有内部类

  1. 内部类是为了更好的封装,把内部类封装在外部类里,不允许同包其他类访问
  2. 内部类中的属性和方法即使是外部类也不能直接访问
  3. 相反内部类可以直接访问外部类的属性和方法,即使private
  4. 利于回调函数的编写。PS:回调函数是函数的迭代
  5. 当描述事物,如身体里的大脑时,大脑在身体内部,可以通过内部类直观描述

内部类特点

  1. 内部类对象不仅指向该内部类,还指向实例化该内部类的外部类对象的内存。
  2. 内部类和普通类一样可以重写Object类的方法,如toString方法;并且有构造函数,执行顺序依旧是先初始化属性,再执行构造函数
  3. 在编译完之后,会出现(外部类.class)和(外部类s内部类.class)两个类文件名。
  4. 内部类可以被修饰为private,只能被外部类所访问。事实上一般也都是如此书写。
  5. 内部类可以被写在外部类的任意位置,如成员位置,方法内。

内部类对象的建立

  1. 在同包其他类以及main方法中(内部类没有private修饰),先创建外部类对象,再通过外部类对象创建内部类

    Out outer = new Out();
    Out.In inner = outer.new In();
    
  2. 通过匿名外部类创建内部类

    Out.In inner = outer.new In();
    
  3. 在外部类里

    可直接创建对象,如In inner=new In();或者直接new In();

内部类访问

  1. 静态时,静态内部类只能访问外部类静态成员;非静态内部类都可以直接访问。(原因是:内部类有一个外部类名.this的指引)当访问外部类静态成员出现重名时,通过(外部类名.静态成员变量名)访问。如Out.show();
  2. 重名情况下,非静态时,内部类访问自己内部类通过this.变量名。访问外部类通过(外部类名.this.变量名)访问。如Out.this.show();
  3. 在没有重名的情况下,无论静态非静态,内部类直接通过变量名访问外部成员变量。

外部类访问

  1. 内部类为非静态时,外部类访问内部类,必须建立内部类对象。

  2. 内部类为静态时,外部类访问非静态成员,通过(外部类对象名.内部类名.方法名)访问,如new Out().In.function()

  3. 内部类为静态时,外部类访问静态成员时,直接通过(外部类名.内部类名.方法名)
    ,如Out.In.funchtion();