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关键字来定义,

  • 只能继承一个父类或实现一个接口
    >- 父类是非静态的类型,则需父类外部实例来初始化
    >- 如果定义在非静态作用域内,会引用外部类实例
    >- 只能捕获外部作用域内的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时,编译器先是查看表达式右边部分,
    也就是所谓的构造器,并将它作为变量的类型,然后将该类型写入字节码当中


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

另外,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表达式替换实现;

关注语言版本的变化

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





最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,739评论 2 9
  • 1 面向对象No6 面向对象 OO Object Oriented 编程时以对象为单元,封装数据和逻辑,以此提...
    征程_Journey阅读 1,132评论 0 2
  • Java 内部类 分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 1、成员内部类: 即作为外部类的一个成...
    ikaroskun阅读 1,223评论 0 13
  • 小胖从官网出发,研究下为什么我们需要些内部类,内部类的区别和联系。思考三个问题:(1)为什么需要内部类?静态内部类...
    小胖学编程阅读 1,641评论 0 6
  • 再次回归一个人的生活,还好这种感觉并不陌生。 还记得抑郁症最严重的时候,是两个月前,那种悲伤的感觉让人想起就毛骨悚...
    缓缓归矣y阅读 258评论 1 2