前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android(Java) | 你真的熟悉Java匿名内部类吗(Java匿名内部类的限制)

Android(Java) | 你真的熟悉Java匿名内部类吗(Java匿名内部类的限制)

作者头像
凌川江雪
发布2019-11-11 13:10:02
1.7K0
发布2019-11-11 13:10:02
举报
文章被收录于专栏:李蔚蓬的专栏李蔚蓬的专栏

要点

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

总结

  • 没有人类认知意义上的名字
  • 只能继承一个父类或实现一个接口
  • 父类是非静态的类型,则需父类外部实例来初始化
  • 如果定义在非静态作用域内,会引用外部类实例
  • 只能捕获外部作用域内的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表达式替换实现;

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 要点
  • 总结
    • a.匿名内部类的名字
      • b.匿名内部类的继承结构
        • c.匿名内部类的构造方法(关注:匿名内部类对外部类的引用)
          • 匿名内部类——父类非静态、所在方法(匿`类被new出来的位置所处的方法)非静态
            • 匿名内部类——父类静态、所在方法非静态
              • 匿名内部类--父类静态、所在方法静态
                • 捕获 匿名内部类 所在方法内(外部作用域) 的 局部变量快照的情况 匿名内部类重写父类方法时,引用到的外部方法体内的局部final变量
                • Lambda转换(SAM转换)
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档