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

要点

匿名类的概念和用法

语言规范以及语言的横向对比等

内存泄漏的切入点

总结

没有人类认知意义上的名字

只能继承一个父类或实现一个接口

父类是非静态的类型,则需父类外部实例来初始化

如果定义在非静态作用域内,会引用外部类实例

只能捕获外部作用域内的final变量

创建时只有单一方法的接口可以用Lambda转换

a.匿名内部类的名字

表面上是没有引用名的,

但其实是有用于定位的“名字”,

3778621348ed如上代码,

new Foo()在定义的时候,

重写了bar()这个方法,

如此一来new Foo(){...}这里就是一个匿名内部类了;

呐这个匿名内部类,实际上在字节码中是会定义出来的,!!!

定义出来一个用于定位的“名字”,

这个“名字”可见上面代码的第二行,

“com.bennyhuo.iiv.ch1.”即代码包名,

“OuterClass$1”即外部内名$1,

1代表这个匿名内部类,

是前缀的外部类中,定义的第一个匿名内部类,

再创建第二个匿名内部类 就是$2了;

所以匿名内部类跟普通类一样,是可以加载出来的!!!

只不过参数格式不一样,

普通类是“class 类名”

匿名内部类是“class 包名.外部类名$num”

3778621348ed

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

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

就默认 匿名内部类 是作为一个子类

去继承其对应的父类了:(接口亦同)

3778621348ed

但是下面这种类型,

既 继承某个父类 又 实现某个接口 的“匿名内部类” 的 这种类型,

在Java中是不被接受的,

因为这其实是一种“或类型”,

即Runnable或上Foo的结果,作为一种类,

这在Java中是不被接受的:

3778621348ed

**即使使用Java 10 的var关键字来定义,

只能继承一个父类或实现一个接口

>- 父类是非静态的类型,则需父类外部实例来初始化

>- 如果定义在非静态作用域内,会引用外部类实例

>- 只能捕获外部作用域内的final变量

>- 创建时只有单一方法的接口可以用Lambda转换

####a.匿名内部类的名字

表面上是没有引用名的,

但其实是有用于定位的“名字”,

3778621348ed

如上代码,

new Foo()在定义的时候,

重写了bar()这个方法,

如此一来new Foo(){...}这里就是一个匿名内部类了;

呐这个匿名内部类,实际上在字节码中是会定义出来的,!!!

定义出来一个用于定位的“名字”,

这个“名字”可见上面代码的第二行,

“com.bennyhuo.iiv.ch1.”即代码包名,

“OuterClass$1”即外部内名$1,

1代表这个匿名内部类,

是前缀的外部类中,定义的第一个匿名内部类,

再创建第二个匿名内部类 就是$2了;

所以匿名内部类跟普通类一样,是可以加载出来的!!!

只不过参数格式不一样,

普通类是“class 类名”

匿名内部类是“class 包名.外部类名$num”

3778621348ed

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

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

就默认 匿名内部类 是作为一个子类

去继承其对应的父类了:(接口亦同)

3778621348ed

- 但是下面这种类型,

既 继承某个父类 又 实现某个接口 的“匿名内部类” 的 这种类型,

在Java中是不被接受的,

因为这其实是一种“或类型”,

即Runnable或上Foo的结果,作为一种类,

这在Java中是不被接受的:

>>

3778621348ed

3778621348ed

嗯,

可是如果实在是想实现一个

如既 继承某个父类 又 实现某个接口 的“匿名内部类”这样的类型,

但要不想占用太多资源,要求同匿名内部类一样用完即销毁,怎么办?

那别用匿名内部类呗,

在方法体内部实现即可,!!!

便可以在方法调用完毕后将其回收,

也可以达到需求:

3778621348ed

另外,Kotlin是可以实现,

既 继承某个父类 又 实现某个接口 的“匿名内部类” 的 这种类型的:

(object类似于class与定义一个引用,

object与后面冒号之间不接名字表示匿名,

冒号后面要继承什么,实现什么,直接写出来就是了)

3778621348ed

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

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

这个可能导致内存泄漏!

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

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

编译器 会 根据代码 为 匿名内部类的构造方法 引入一些参数,

如下面图中例子,

(右上)有一个OuterClass,里边有一个InnerClass,

(左上)这时候在Client类中,

new出来一个匿名内部类,

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

例子中这个new出来的匿名内部类,

实际上它的父类就是InnerClass,

而InnerClass本身是一个非静态的内部类,

!!!!!非静态的内部类本身就会引用外部类的实例!!!!!!,

所以OuterClass()的实例也会在这里(左上第四行)new出来;

而下方的Client$1就是上述所说的匿名内部类的类型了,

Client$1的命名格式其实就是刚刚说的外部内名$匿名内部类序号;

所以图中下方代码块的第二行,

即编译器 根据代码 为 匿名内部类生成的构造方法,

其第一个参数,就是Client,即匿名内部类所在方法 对应的 外部作用域(最外部类);

因为这里的匿名内部类所在的方法 是非静态方法,

所以一定是被某个实例(最外部类实例) 引用着的!!!!!

其第二个参数,即匿名内部类的父类,

这个父类如果是某外部类的非静态内部类,

那把这个对应的外部类实例传进来即可,

因为这个外部类实例可以应用到其成员(包括非静态内部类);

3778621348ed

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

interface跟静态内部类的效果是差不多的,

就是静态的,

也就是不会去引用其外部类的实例!!!!!

所以这时候匿名内部类的构造方法的参数

就只有一个所在方法的最外部类实例了;

3778621348ed

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

而,当匿名内部类所在的方法是静态的,

则其构造方法的参数中,

不存在所在方法的最外部类实例了;

3778621348ed

即,

匿名内部类的构造方法的参数个数,

由其父类以及其所在方法 是否静态决定,

父类非静态,则需传入父类相关实例;

所在方法非静态,则需传入所在方法的最外部类实例;

反则,

哪个静态了,就不用传哪个;

00 01 10 11

捕获 匿名内部类 所在方法内(外部作用域) 的 局部变量快照的情况

匿名内部类重写父类方法时,引用到的外部方法体内的局部final变量

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

虽然说如果不final的话,

对匿名内部类的构造方法也不是很有影响,

因为传给匿名内部类构造方法的这个局部变量实例,

实际上只是捕获局部变量实例的一个快照,

快照即复制一份引用,给匿名内部类的构造方法去使用,

但是,

如果这个局部变量实例不是final的,

那局部变量实例被重新赋值了,

外部的局部变量实例跟传给匿名内部类构造方法的局部变量实例,就不一样了!

这个肯定是不行的吧,

所以,

Java要求,

这个地方(图中左边,第三行,也即被传给 匿' 构造方法 的这个 局部实例)一定要是final的,

原因就是为了让这个局部变量实例,在外部和在 匿’ 构造方法 中,

给保持一致!

3778621348ed

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

是编译器生成的

参数列表包括

外部对象(定义在非静态域内)

父类的外部对象(父类非静态)

父类的构造方法参数(父类有构造方法且参数列表不为空)

外部捕获的变量(方法体内有引用外部final变量)

事实上是可以通过反射,

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

Lambda转换(SAM转换)

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

3778621348ed

3778621348ed

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

关注语言版本的变化

体现对技术的热情

体现好学的品质

显得专业

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值