c) 通过以上的步骤我们完成了对Test类中method方法的调用。但是重点就来了,如果学会了匿名内部类,通过使用匿名内部类,我们就可以一步做到以上定义Inner子类和创建其子类对象这两个工作。
3. 那么该怎么用呢?
a) 首先要搞清楚两点:
i. 创建匿名内部类的前提是有一个父类或者父接口,而我们待会创建出来的匿名内部类会是这个父类或者父接口的子类。
ii. 匿名内部类指的是没有类名的内部类,那么一个类没有名字我们该如何创建对象呢?答案是:我们要将定义该接口的子类和创建其对象两个步骤揉在一起。
b) 匿名内部类的定义格式:
new 父类/父接口名(){
//大括号相当于子类的定义
}
ii. 格式解读:
1. new 父类/父接口():其实是创建了一个此父类或者父接口的子类对象
2. {}:大括号中是对这个子类的定义,和我们通常定义类没有什么区别。如果父类或者父接口中有抽象方法,应该在区域此实现。(如下图所示,Inner这个接口中有一个抽象方法,于是我们就在这对大括号中实现了这个抽象方法)
iii. 那么到了这一步,我们就已经创建出了匿名内部类,这相当于同时完成了定义类和创建对象的两个步骤。
c)使用匿名内部类:
i. 接下来我们如果要调用Test类中的method方法就可以按照上面的格式定义出一个匿名内部类了。
ii. 注意:这个匿名内部类整体就是一个对象,所以我们直接就可以把它当作调用方法时传递的实际参数。
d) 那么以上我们就已经完成了对匿名内部类的定义和使用。接下来我们还需要去了解一些使用中的注意事项。 4. 注意事项:
a) 首先我们要明白的是,匿名内部类其实就相当于一个定义在方法中的类,有时候我们除了直接使用匿名内部类外,也可能会在匿名内部类的方法中使用外部类方法中的一些变量。
b) 但是,当我们直接使用此匿名内部类所在方法的局部变量,显示编译错误:
那么,究竟是什么原因导致了匿名内部类不能直接使用外部类方法中的局部变量呢?一起去探个究竟。
c) 问题探究:
i. 局部变量的作用域是在此方法中,当这个方法调用结束,这个方法中所有的局部变量也应该在内存中消失。
ii. 对于对象而言,只要有引用指向一个对象,这个对象就不会变成垃圾。而匿名内部类本质就是一个对象,当方法的外部有其他引用指向这个对象的时候,哪怕其所在的方法调用完毕,这个对象也还会存在堆内存中。
iii. 而作为匿名内部类中的方法,只要对象存在,这个方法就有可能被调用,而在上图所示的代码中,如果我们调用匿名内部类中的这个方法,就会要打印num变量值,但是如果方法已经调用完毕,num 这个变量是应该消失的。相当于 我们会用一个存在的对象调用方法去打印一个已经消失的变量。
iv. 怎么办呢?接着往下看。
d) 解决方案
i. 其实作为代码编辑器的eclipse已经给了我们很友好的提示,解决方案其实很简单,就是定义变量num的时候用final修饰,使其成为一个常量,作为常量,其值是一直不会改变的。
ii. 所以当方法调用完毕,num从内存中消失,我们再去调用匿名内部类中的方法打印num变量的时候,直接相当于打印一个常量100,程序的运行也不会受到影响。