Android(Java) | 你知道吗?Java匿名内部类其实是有“名字”和构造方法的!

简介: Android(Java) | 你知道吗?Java匿名内部类其实是有“名字”和构造方法的!

要点

  • 匿名类的概念和用法
  • 语言规范以及语言的横向对比等
  • 内存泄漏的切入点

总结

  • 没有人类认知意义上的名字
  • 只能继承一个父类或实现一个接口
  • 父类是非静态的类型,则需父类外部实例来初始化
  • 如果定义在非静态作用域内,会引用外部类实例
  • 只能捕获外部作用域内的final变量
  • 创建时只有单一方法的接口可以用Lambda转换



a.匿名内部类的名字

表面上是没有引用名的,
但其实是有用于定位的“名字”,
**如上代码,
new Foo()在定义的时候,
重写了bar()这个方法,
如此一来new Foo(){...}这里就是一个匿名内部类了;

呐这个匿名内部类,实际上在字节码中是会定义出来的,!!!
定义出来一个用于定位的“名字”,
这个“名字”可见上面代码的第二行,
com.bennyhuo.iiv.ch1.”即代码包名
OuterClass$1”即外部内名$1
1代表这个匿名内部类
是前缀的外部类中,定义的第一个匿名内部类,
再创建第二个匿名内部类 就是$2了;

所以匿名内部类普通类一样,是可以加载出来的!!!
只不过参数格式不一样,
普通类是“class 类名
匿名内部类是“class 包名.外部类名$num”**





b.匿名内部类的继承结构

  • **匿名内部类被创建的时候,

就默认 匿名内部类 是作为一个子类
继承其对应的父类了:(接口亦同)**

  • **但是下面这种类型,

既 继承某个父类 又 实现某个接口 的“匿名内部类” 的 这种类型,
在Java中是不被接受的,
因为这其实是一种“或类型”,
即Runnable或上Foo的结果,作为一种类,
这在Java中是不被接受的:**

**即使使用Java 10 的var关键字来定义,
也是不行的,
这种类型还是不能被接受:
([在处理 var时,编译器先是查看表达式右边部分,
也就是所谓的构造器,并将它作为变量的类型,然后将该类型写入字节码当中]( https://www.cnblogs.com/feichen-2018/p/8650216.html))**

**嗯,
可是如果实在是想实现一个
既 继承某个父类 又 实现某个接口 的“匿名内部类” 这样的类型,
但要不想 占用太多资源,要求同 匿名内部类一样 用完即销毁,怎么办?
那别用 匿名内部类呗,
方法体内部实现即可,!!!
便可以在方法调用完毕后将其 回收
也可以达到 需求:**

**另外,Kotlin是可以实现,
既 继承某个父类 又 实现某个接口 的“匿名内部类” 的 这种类型的
(object类似于class与定义一个引用,
object与后面冒号之间不接名字表示匿名,
冒号后面要继承什么,实现什么,直接写出来就是了)**





c.匿名内部类的构造方法(关注:匿名内部类外部类引用

  • **匿名内部类会有外部类的引用,

这个可能导致内存泄漏!**

  • **匿名内部类构造方法编译器 帮忙定义的!!!

开发者没有权 定义匿名内部类构造方法;**

**编译器 会 根据代码 为 匿名内部类构造方法 引入一些参数,
如下面图中例子,
(右上)有一个OuterClass,里边有一个InnerClass
(左上)这时候在Client类中,
new出来一个匿名内部类
**

匿名内部类——父类非静态、所在方法(匿`类被new出来的位置所处的方法)非静态

**例子中这个new出来的匿名内部类
实际上它的父类就是InnerClass
而InnerClass本身是一个非静态的内部类
!!!!!非静态的内部类本身就会引用外部类的实例!!!!!!
所以OuterClass()的实例也会在这里(左上第四行)new出来;

而下方的Client$1就是上述所说的匿名内部类的类型了,
Client$1的命名格式其实就是刚刚说的外部内名$匿名内部类序号

所以图中下方代码块的第二行,
编译器 根据代码 为 匿名内部类生成的构造方法

第一个参数,就是Client,即匿名内部类所在方法 对应的 外部作用域(最外部类);
因为这里的匿名内部类所在的方法非静态方法
所以一定是被某个实例(最外部类实例) 引用着的!!!!!

第二个参数,即匿名内部类的父类
这个父类如果是某外部类非静态内部类
那把这个对应的外部类实例传进来即可,
因为这个外部类实例可以应用到其成员(包括非静态内部类);**



匿名内部类——父类静态、所在方法非静态

interface静态内部类的效果是差不多的,
就是静态的,
也就是不会去引用其外部类的实例!!!!!
所以这时候匿名内部类构造方法的参数
就只有一个所在方法的最外部类实例了;



匿名内部类--父类静态、所在方法静态

**而,当匿名内部类所在的方法是静态的,
则其构造方法的参数中,
不存在所在方法的最外部类实例了;**

**即,
匿名内部类构造方法参数个数
由其 父类以及其 所在方法 是否 静态决定,
父类 非静态,则需传入 父类相关实例
所在方法 非静态,则需传入所在方法的 最外部类实例
反则,
哪个静态了,就不用传哪个;
00 01 10 11**



捕获 匿名内部类 所在方法内(外部作用域) 的 局部变量快照的情况
匿名内部类重写父类方法时,引用到的外部方法体内局部final变量

**通常,要求要被捕获的局部变量 需要是final修饰的;

虽然说如果不final的话,
匿名内部类构造方法也不是很有影响
因为传给匿名内部类构造方法的这个局部变量实例
实际上只是捕获局部变量实例的一个快照
快照即复制一份引用,给匿名内部类构造方法去使用,

但是,
如果这个局部变量实例不是final的,
局部变量实例重新赋值了,
外部的局部变量实例跟传给匿名内部类构造方法局部变量实例,就不一样了!
这个肯定是不行的吧,

所以,
Java要求,
这个地方(图中左边,第三行,也即被传给 匿' 构造方法 的这个 局部实例一定要是final的,
原因就是为了让这个局部变量实例,在外部和在 匿’ 构造方法 中,
保持一致!**

匿名内部类的构造方法小结

  • 是编译器生成的
  • 参数列表包括

    • 外部对象(定义在非静态域内)<如上的Client>
    • 父类的外部对象(父类非静态)<如上的OuterClass>
    • 父类的构造方法参数(父类有构造方法且参数列表不为空)
    • 外部捕获的变量(方法体内有引用外部final变量)<如上的Object>
  • **事实上是可以通过反射

去修改匿名内部类构造方法持有的外部引用参数列表)的**



Lambda转换(SAM转换)

  • SAM--single abstract method 单一方法类型

一个接口,只有一个抽象方法时,可以用Lambda表达式替换实现;

关注语言版本的变化

  • 体现对技术的热情
  • 体现好学的品质
  • 显得专业





相关文章
|
5天前
|
Java 程序员 数据库连接
Java执行顺序大揭秘:静态块、非静态块和构造方法谁先谁后?
本文详细介绍了Java中的初始化块,包括静态初始化块和非静态初始化块的概念、执行顺序和实际应用场景。通过具体示例,帮助读者理解这两种初始化块的区别和使用场景,让面试官对你刮目相看。
12 0
Java执行顺序大揭秘:静态块、非静态块和构造方法谁先谁后?
|
24天前
|
设计模式 Java 测试技术
Java零基础-构造方法详解
【10月更文挑战第5天】Java零基础教学篇,手把手实践教学!
16 1
|
1月前
|
Java
java构造方法时对象初始化,实例化,参数赋值
java构造方法时对象初始化,实例化,参数赋值
43 1
|
2月前
|
Java API
java调用构造方法
java调用构造方法
37 3
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
109 1
|
1月前
|
Java
java构造方法的两种传值调用对实参的影响
java构造方法的两种传值调用对实参的影响
26 0
|
1月前
|
Java 程序员 编译器
【Java】继承、super、final、子类构造方法
【Java】继承、super、final、子类构造方法
26 0
|
3月前
|
Java
【Java基础面试十九】、构造方法能不能重写?
这篇文章指出Java中的构造方法不能被重写,因为构造方法必须与类名相同,而重写要求子类方法与父类方法同名,允许构造方法重写将违背这一规则。
【Java基础面试十九】、构造方法能不能重写?
|
3月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
3月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
68 1