Java基础系列(三十):局部内部类,匿名内部类
What
局部内部类就是定义在某个方法内部的内部类,它的作用域仅限于这个方法。
Why
当我们在外围类中定义的内部类仅仅在某个方法中使用了一次,这种情况下,我们就可以选择去使用局部内部类。
How
以上节课的例子继续讲解,由于TestListener这个内部类仅仅在start方法中使用了一次,所以我们在这里可以使用局部内部类。
public class InnerClassTest {
private Integer times;
private boolean beep;
public InnerClassTest(Integer times, boolean beep) {
this.interval = interval;
this.beep = beep;
};
public void start(){
class TestListener implements ActionListner {
public void actionPerformed(ActionEvent event) {
System.out.println("TestListener is running");
if (beep) {
Tookit.getDefaultToolkit().beep();
}
}
}
ActionListener listener = new TestListener();
Timer t = new Timer(times, listner);
t.start();
} }
这里需要注意,局部类不可以使用public或者private访问修饰符进行声明,因为它作用域仅仅被限定在声明这个局部类的块中。
局部类有一个优势,它可以对外部世界完全的隐藏,即使他的外部类中的其他模块也不可以访问它,除了start
方法以外,没有任何方法知道这个内部类的存在。
外部方法访问变量(进阶)
与其他的内部类相比,局部类还有一个其他内部类所不具备的有优点。它不仅可以访问包含它们的外部类,还可以访问局部变量,但是这些局部变量必须声明为final,它们一旦被赋值,就不能被改变。
下面我们接着来改变上面的那个栗子:
public void start(int times, boolean beep){
class TestListener implements ActionListner {
public void actionPerformed(ActionEvent event) {
System.out.println("TestListener is running");
if (flag) {
Tookit.getDefaultToolkit().beep();
}
}
}
ActionListener listener = new TestListener();
Timer t = new Timer(times, listner);
t.start();}
我们可以看到,外围类不在需要去存储实例变量beep了,它只是引用start方法中的参数。 接下来我们来深入了解这个方法的控制流程:
- 调用
start
方法 - 调用内部类的构造器,初始化对象变量
listener
- 将
listener
引用传递给Timer构造器,定时器开始计时,start
方法结束。此时,start
方法中beep
变量被回收。 - 然后
actionPerformed
方法执行if(beep)
。
看到这里,我相信大部分人会有疑问,为什么beep变量被回收,但是actionPerformed
方法仍然可以调用到这个方法?
实际上,内部类在beep域被释放之前将beep域用start方法中的局部变量进行备份,我们接下来来看一下反编译后的内部类,来证实我们的猜测:
class InnerClassTest$TestListener {
public InnerClass$TestListener(InnerClassTest, boolean);
public void actionPerformed(java.awt.event.ActionEvent);
final boolean val$beep;
final InnerClassTest this$0;
}
请注意构造器的boolean
参数和val$beep
实例变量。当创建一个对象的时候,beep就会传递给构造器,并存储在val$beep域中。编译器必须检测对局部变量的访问,为每一个变量建立相应的数据域,并将局部变量拷贝到构造器中,以便将这些数据域初始化为局部变量的副本。
匿名内部类
匿名内部类其实就是对局部内部类的一个深化的应用,如果我们只是需要创建这个类的一个对象,那么我们完全不必去给这个类命名,这种类就被称为匿名内部类。
接下来,我们接着对上面的例子进行改编:
public void start(int times, boolean beep){
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("TestListener is running");
if (flag) {
Tookit.getDefaultToolkit().beep();
}
}
}
Timer t = new Timer(times, listner);
t.start();}
这段语句的含义是:创建一个实现ActionListener接口的类的新对象,需要实现的方法定义在括号内。
通用的语法格式是:
new SuperType(constrution params) {
inner class methods and data
}
其中SuperType
既可以是接口,那么内部类就要去实现这个接口,它同样可以是一个类,那么内部类就要去扩展它。
由于构造器的名字必须与类名相同,但是匿名类并没有类名,所以,匿名类不能有构造器。取而代之的是,将构造器参数传递给父类构造器。尤其是在内部类实现接口的时候,不能有任何构造参数。
如果构造参数的闭小括号后跟的是单引号,那么就是在构造一个类的新对象,如果说构造参数的闭小括号后面跟一个开大括号,正在定义的就是匿名内部类。
静态内部类(仅供了解)
有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外部类对象。所以可以把内部类声明为static,以便取消产生的引用。
只有内部类可以声明为static,静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他所有内部类完全一样。
与常规内部类不同的地方是,静态内部类可以有静态域和方法,声明在接口中的内部类自动生成static和public类。
下节预告
有关内部类的知识,我们就学习到这里,这些已经足够我们日常开发使用了。下一节我们来学习有关异常的知识~
- Python文件夹与文件的操作
- ExpandableListView实现商品列表折叠
- Swift 3.0介绍
- IntelliJ IDEA 安装目录的核心文件讲解
- 详述 IntelliJ IDEA 的使用界面
- WCF系列教程之消息交换模式之请求与答复模式(Request/Reply)
- Koa-router源码解读
- WCF系列教程之初识WCF
- IntelliJ IDEA 缓存和索引的介绍及清理方法
- Node.js原理
- WCF系列教程之WCF消息交换模式之单项模式
- React Native调用Android相机图库
- IntelliJ IDEA 之 HelloWorld 项目创建及相关配置文件介绍
- 设置 IntelliJ IDEA 主题和字体的方法
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Vue兄弟组件传值
- Vue设置浏览器的标题title和图标icon
- VantUI封装自定义Tabbar路由跳转
- 听说Mysql你很豪横?-------------分分钟带你玩转SQL高级查询语句(常用查询,正则表达式,运算符)
- jQuery实现点击添加样式同胞移除样式
- 微信小程序生命周期
- 听说Mysql你很豪横?-------------分分钟带你玩转SQL高级查询语句(库函数,存储过程)
- 微信小程序下拉刷新上拉加载
- 微信小程序引用自定义组件
- 听说Mysql你很豪横?-------------搭建MySQL MHA实现数据库高可用( MySQL MHA概述、 搭建 MySQL MHA、 MySQL MHA 故障切换)
- jQuery点击click()事件
- 大点干!早点散----------深入剖析LVS负载均衡群集原理
- javascript中元素的scrollLeft和scrollTop属性说明
- 大点干!早点散----------LVS负载均衡之LVS-NAT部署实战
- 大点干!早点散----------负载均衡LVS-DR群集部署