匿名内部类的介绍
01:匿名内部类 顾名思义 是没有名字,所以就造成了 , 匿名内部类 没有构造函数喽。
02:没有构造函数 我们怎么对这个匿名内部类 创造对象呢?我们使用它的 抽象父类 或者 常规父类 或者接口的名字 前面加上 new的形式创造对象一个匿名的对象 来赋值给 父类型的引用。
----备注:我们可以使用 常规类 抽象类 接口 来构造匿名内部类。
比如:
03:匿名内部类 在一个外部类中 所以 无法像 常规内部类那样 直接 写的明明白白的,我们经常使用这个匿名内部类干什么? 使用这个匿名内部类 给它的 父类【常规类 , 抽象类】 或者 接口 声明的引用变量 赋值 !!!!
04:匿名内部类 没有名字 ,主要作用是 使用 关键字new 父类 或者 接口 名字 加 括号 的形式 ,来创建一个匿名的对象,因为没有名字,并且这个匿名类的模板也是随时创建随时使用,一次性的东西,给他弄一个静态变量和静态方法不合适, 另一个角度的看法,因为没有名字,在java虚拟机中 我们是通过 名字 来给这个 类模板 创造这个 类模板 Class对象的, 啥都没有无法创建呀,从这个角度来讲 并不能够给他 弄一个静态的成员。
===》匿名内部类 不可以又静态成员的!!!
04:匿名内部类 的 内部的成员内容:
A:不可以存在 静态成员
B: 可以有自己的 实例数据域,但是这些数据域 无法被 引用变量使用,因为没有名字,使用的是父类 或者接口的名字, 按照声明变量的类型分配变量, 所以分配不到 匿名内部类自己的成员实例数据域。
B1:但是我们可以在匿名内部类的实例方法中 , 使用自己声明的 变量 , 完成一些辅助性运算。
B2:匿名内部类的数据域 如果 隐藏了 父类的数据域,我们可以使用super的形式去调用父类的数据域 。使用匿名内部类中自己定义的变量 可以使用this。
B3:使用this来调用自己 隐藏了 父类同名的 自己的 数据域 , 或者自己新声明的数据域。
===》匿名内部类 不可以具有静态的成员,调用父类 或者父接口的成员的时候 斟酌使用 名字 或 super。
B4:匿名内部类 中 也可以 调用父类的 静态方法和静态数据域,使用的是父类 或者父接口【接口中可以有 静态方法 !!!Jdk1.8之后的新功能】的名字。
C:匿名内部类,可以的主要作用是实现抽象[父]类 或者 接口中的 抽象方法,或者重写常规父类中的方法(重写这个方法,定义这个方法有更加 适合的操作! ===》 这是最主要的操作!!!), 但是也可以有自己的实例方法。
C1:但是 匿名内部类 自己的实例方法 ,但是不可以被 引用变量使用 , 因为是按照声明变量来分配方法的,父类中没有匿名内部类 自己定义的方法。 但是可以在自己的类中 被其他重写的方法 调用 匿名内部类自己的实例方法。 ====》因为运行起来的时候,根据父类 或者父接口 进行变量的指派和动态调用的。
D:它主要 是 使用父类 或者 接口 来创造一个对象 赋值给 父类的引用变量的, 总体来看依然是一个赋值语句,所以后面要加上 分号。
E:虽然 他不可以有自己的构造函数 ,但是创造匿名对象的时候,依然 调用了父类的构造函数,并且先调用父类的构造方法 创造一个父类对象。 这里是之前写的内容, 如果是一个接口的话,是不是就错了???
F: 就当作是给 一个父类 引用变量 赋值, 因此 无论将匿名类使用在 任何地方都可以喽。
01:使用在一个类的 作用域中, 作为这个类的 实例变量 或者 是静态变量。
01.1:作为实例变量, 可以直接访问 外部类的 实例数据据 和 静态数据【通过外部类名字】 //但是此时要注意了,这个匿名内部类依旧不是 静态的类。
01.2: 作为静态变量,即便是作为静态变量,但是 这个类[匿名内部类]的前身依旧不是 静态的类 而是一个常规类 或者 抽象类 或者是一个接口 [java规范],内部类 内部依旧不可以 有静态的变量 静态的方法 【下面详细解释】 , 但是还可以访问到 外部类的 静态变量 ,but无法直接访问到外部类的实例变量了【除非new一个 外部类变量】。
//但是此时要注意了,这个匿名内部类依旧不是 静态的类。
02:用到 实例方法中 或者是 用到静态方法中,那么形式 只可以被写作 临时变量了【不可以加上 可见性修饰符 静态修饰符了】 。
02.1:当用到 实例方法中的时候 我们可以直接访问到 这个方法内部的 临时变量[要加上final关键字],也可以直接访问 外部类的实例变量和静态变量了 。
02.2:当用到 静态方法中的时候 我们 仅仅可以直接 访问到 外部类的 的 静态变量, 和 当前这个方法内部的临时变量。 如果要访问到 外部类的实例变量 就需要 new一个新的外部类对象了。
---->:下面解释一下 为什么在静态方法中 这个匿名内部类 不可以有静态的元素:
01:看报错:
//解释:也就是说 在加载Comparables接口【这个接口中 我们有静态方法 静态变量 】的时候 已经是加载完成了 这个是一次性【第一次】的加载的,而此时 我们在 匿名内部类中 又一次 写了一个静态的元素【方法或者是变量】, 此时虚拟机无法再次加载了,加载机制只可以启动一次,也就是说 如果我们 我们需要将这些 静态的方法和变量 提前加载的话 , 需要提前卸载 接口 抽象类 后者常规类中 好让他在加载接口 抽象类 或者常规类的时候 一并加载 ,而不是 写在匿名内部类中,因为虚拟机只会加载一次,所以就出现了 错误信息。
推广一下来说: 匿名内部类 无论是作为 实例变量 静态变量 静态方法 实例方法中 都不可以有静态的元素!!!
G: 匿名内部类只可以实现一个接口 或者 一个常规类 ,或者一个抽象类 ,但是被使用的接口 或者常规类 或者抽象类 可以实现很多的接口 , 和 很多的抽象类 , 接口也可以继承更多的接口, 只是规定 这个匿名函数在使用的时候 只 可以使用上述 一个接口或者类(可以是抽象的)。 之后 必须要实现他们中所有的 抽象方法, 或者重写父类中的某些个方法, 因为匿名内部类 再次被定义为 抽象的 不合适 也不合法。
H:前提是 所被使用的类 或者 接口 能被当前的类 看到,要考虑可加性的问题。
05:使用匿名内部类的主要作用是创造对象用的,因此来说 这个对象的位置 可以是任意的位置:
外部类的实例变量, 外部类的静态变量, 外部类的实例方法内部, 外部类的静态方法内部。 也可以是方法的参数。
给实例变量赋值 , 实例方法内部赋值,以及 给静态变量赋值 ,静态方法内部赋值, 跟普通引用变量赋值时一回事, 以及给 方法 参数赋值也是一样的!!!
参数形式的匿名函数:
06:关于匿名内部类 重写的问题:
01: 我们使用 new + 接口/抽象类/常规类 的形似去 构造一个匿名内部类 对象。
03: 这个匿名内部类 没有自己的模板 我们是 借用 其他的模板去 构造的对象。
04:因此我们 在给这个匿名内部类 申明类型的 时候 使用的是 接口/抽象类/常规类 的类型。
05:我们借用 接口/抽象类/常规类 的模板去 构造一个匿名内部类对象 ===》重写 其中的 方法 =====》构造 一个匿名内部类对象。 记住了 这里是对象 。
06:因此我们 调用 匿名内部类对象的时候 依然是使用 java虚拟机的 动态绑定机制。
07:此时的动态绑定机制 是从 被借用的模板 接口/抽象类/常规类 指定的方法开始 然后往下 寻找 实现的具体 的方法。 这里尤其要注意 要考虑到 接口/抽象类/常规类 方法的 可见性!!! 这个决定能否 能够绑 定到具体的实现方法上。
08:这里 牵扯到 方法的重写 ,只有我们在实现继承 或者接口的 时候才会牵涉到重写的问题。 当我们使用匿 名内部类对象的时候也 使用到了重写方法。首先要考虑的问题:A: 被重写的方法是否 可以被访问到
09:我们可以为 匿名内部类 也幻想成 一个具体的类 这个类没有名字 跟当前的外部类 所处同一个位置。
===> 在调用的时候 因为没有名字 我们只好使用 接口/抽象类/常规类 的类作为 匿名内部类对象的 类型。所以在实现 动态绑定的时候 我们 根据 声明类型去分配方法的问题 此时要考虑到 父类中方法的可见性。
比如 : 接口在另一个a包中 有一个抽象的 protected方法, 外部类和匿名内部类 在b包中 重写了那个protected 方法为 public方法。 但是我们在b包的外部类中 调用 这个你们那个内部类的重写的方法的时候 会报错,提示信息是 找不到 看不到!!!
------> : 这里参考 另一个笔记中 写的 动态绑定 和 分配方法的 分析和结论 。