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方法中的参数。
接下来我们来深入了解这个方法的控制流程:
1. 调用start方法
2. 调用内部类的构造器,初始化对象变量listener
3. 将listener引用传递给Timer构造器,定时器开始计时,start方法结束。此时,start方法中beep变量被回收。
4. 然后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类。


公众号

扫码或微信搜索 Vi的技术博客,关注公众号,不定期送书活动各种福利~

Java基础系列(三十):局部内部类相关推荐

  1. Java基础系列三之继承

    继承 一.什么是继承 继承是一个类继承另一个类,这个类拥有被继承的类中所有的成员方法(除了父类的构造方法)和属性: 继承是面向对象特征之一: 实现继承的类称为子类或者派生类,被继承的类称为父类,或者称 ...

  2. Java基础(三十四)String、StringBuffer类和数据缓冲区Buffer类

    一.String类 1.创建字符串对象 创建字符串对象有两种方法:直接用"="或者使用"new String(...)" String aStr = " ...

  3. Java基础(三十二)JDBC(2)连接数据库

    一.连接数据库的过程 连接数据库的过程:加载数据库驱动程序,不过只需在第一次访问数据库时加载一次,然后在每次访问数据库时创建一个Connection实例,然后执行操作数据库的SQL语句,并返回执行结果 ...

  4. Java基础系列(十四)常用API

    常用API 一.基本类型包装类 1.1.将字符串转成基本类型 1.2.将基本类型转成字符串 二.System类 三.Math类 四.Array类 一.基本类型包装类 基本数据类型对象包装类特点:用于在 ...

  5. Java基础(三十五)Math、Random类和数字格式化(String.format方法)

    一.Math类 Math类常用的方法: public static long abs (double a) 返回a的绝对值 public static double max (double a,dou ...

  6. Java 基础系列(二十二) --- Maven到底是什么? 如何使用

    Maven 1 以前 2 现在 2.1 背景 2.2 新建 Maven 2.3 解读 Maven 项目 2.4 使用 Maven 2.4.1 管理依赖 2.4.2 打包构建 1 以前  举个例子, 在 ...

  7. 大数据必学Java基础(三十九):面向对象三大特性之一封装(Encapsulation)

    文章目录 面向对象三大特性之一封装(Encapsulation) 一.生活案例 二.Java中封装的理解

  8. 大数据必学Java基础(三十):IDEA的使用介绍

    文章目录 IDEA的使用介绍 一.IDE ❀优点 ❀缺点 二.JetBrains公司介绍

  9. Java基础系列五 面向对象程序设计

    Java基础系列第一章 初识Java语言 Java基础系列第二章 Java语言基础 Java基础系列第三章 Java流程控制 Java基础系列第四章 Java数组与字符串 Java基础系列第五章 面向 ...

  10. java基础系列十七(时间日期转换)

    目录 一.概述 二.基础知识 2.1 时区 2.2 格林威治时间 2.3 时间戳 三.Date/Calendar/SimpleDateFormat 3.1 Date 3.2 Calendar 3.3 ...

最新文章

  1. 建立名称server
  2. 高级算法专家储开颜:无端不视频 阿里视频云三大端上技术能力
  3. 01 使用numpy处理数据、ndarry创建
  4. Python 使用CORS跨域资源共享解决flask服务器跨域问题、浏览器同源策略
  5. html中基本选择器的优先级,CSS_CSS中的各种选择器与样式优先级小结,优先级:由高到低(从上到下)- phpStudy...
  6. 汇编程序.c到a.out的生命历程
  7. C#验证IP是否为局域网地址的三种方法
  8. 全面系统地总结Linux的基本操作(下)
  9. log解析工具 px4_console.log(console.log) = ?
  10. 基于JAVA+SpringMVC+Mybatis+MYSQL的现代史网站设计
  11. oracle视图分页查询,关于Oracle数据库各种操作大全(四)分页(视图)
  12. redis永久化存储
  13. css基础 -文本溢出 text-overflow:ellipsis;
  14. Dirichlet Process 和 Dirichlet Process Mixture模型
  15. 去掉scan pattern多余port的方法
  16. latex 小于_小于等于 latex
  17. 世界地图展开图,来自 Simon's World Map
  18. git提交代码出现 fatal: The remote end hung up unexpectedly-错误处理
  19. MySQL数据库知识点
  20. QQ传输文件原理参考(来自互联网)

热门文章

  1. P1567 吃糖果游戏
  2. java map 参数传递_Java参数传递分析
  3. css背景视差_具有JavaScript和CSS转换的廉价视差
  4. 日志服务与日志分析工具
  5. 十一、Latex的数学矩阵排版
  6. 微信小程序image图片自适应宽度比例显示的方法
  7. 转: RGB565、RGB888互相转换原理和代码
  8. VPX信号处理板VPX3U-2DSP-C6678
  9. Microsoft Word 设置底纹
  10. Maven 项目查找 jar 包是由哪个依赖引入的