内部类
内部类是定义在另一个类中的类。
定义内部类的原因有:
1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
2)内部类可以对同一个包中的其他类隐藏起来。
3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
使用内部类访问对象状态
分析一下下面一段代码
public class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval,boolean beep) {……} public class TimerPrinter implements ActionListener {……} }
其中TimePrinter位于TalkingClock类的内部,并不代表每个TalkingClock都有一个TimePrinter实例域。TimePrinter是由TalkingClock类的方法构造。
在计算机中内部类的对象总有一个隐式引用,他只想了创建它的外部类对象。这个引用在内部类的定义中是不可见的。然而,为了说明这个概念,可以将外围对象的引用称为outer。所以那些实例域就可以outer.实例域。
outer引用是在内部类的构造器中设置的,编译器为所有的内部类构造器添加了一个外部类引用的参数。这里的outer只是一个借代,并非真的是outer,只是表示外部类的引用。
下面是一个测试内部类的完整程序:
package com.java.innerClass; import javax.swing.JOptionPane; public class InnerClassTest { public static void main(String[] args) { // TODO Auto-generated method stub TalkingClock clock = new TalkingClock(1000,true); clock.start(); JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } }
package com.java.innerClass; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Date; import javax.swing.Timer; class TalkingClock { private int interval; private boolean beep; public TalkingClock(int interval,boolean beep) { this.interval=interval; this.beep=beep; } public void start() { ActionListener listener=new TimePrinter(); Timer t = new Timer(interval,listener); t.start(); } public class TimePrinter implements ActionListener{ public void actionPerformed(ActionEvent event) { System.out.println("At the tone,this time is"+new Date()); if(beep) Toolkit.getDefaultToolkit().beep(); } } }
内部类的特殊语法规则
其实用表达式OuterClass.this来表示外围类引用。其中OuterClass表示的是外部类的类名。
如果在外部类的作用域之外要使用OuterClass.InnerClass
内部类是否有用、必要和安全在Java1.1增加内部类时,违背了Java简单的设计理念。内部类是一种编译器现象。与虚拟机无关,编译器会把内部类翻译成用$分隔外部类名与内部类的常规内文件,而虚拟机则对此一无所知。
在TalkingClock类内的TimerPrinter类将被翻译成类文件TalkingClock$TimePrinter.class。我们可以通过原先内部类和 将内部类调出变为常规类之后再用引用 将其调用发现,内部类可以访问外部类的私有数据,但是调出去的常规类就无法完成了。这也从侧面说明内部类拥有访问特权。与常规类功能相比起来功能更加强大。至于安全问题,在绝大部分情况下是安全的,书中说的方法,我大致看懂了,但是我觉得这个并没有总结的必要,知道其中原理就好。
局部内部类
如果仔细地阅读TalkingClock实例方法就会发现,TimerPrinter这个类名字只在start方法中创建这个类型的对象时使用了一次。
当遇到这类情况时,可以在一个方法中定义局部类。
public void start(){ class TimePrinter implements ActionListener { public void actionPerformed(ActionEvent event){ System.out.println("At the tone,the time is"+new Date()); if(beep) Toolkit.getDefaultToolkit().beep(); } } ActionListener listener = new TimerPrinter(); Timer t=new Timer(interval,listener); t.start(); }
局部类不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。
局部类有一个勇士,即对外部世界可以完全地隐藏起来。
由外部方法访问变量
与其他内部类相比较,局部类还有一个有点。他们不仅能够访问包含它们的外部类,还可以访问局部变量,不过这些局部变量必须事实上为final,即它们赋值之后绝对不会改变。。
public void start(int interval,boolean beep) { class TimerPrinter implements ActionListener{ public void actionPerformed(ActionEvent vent){ System.out.println("At the tone,this time is"+new Date()); if(beep) Toolkit.getDefaultToolkit().beep(); } } ActionListener listener =new TimePrinter(); Timer t = new Timer(interval,listener); }
请注意,TalkingClock类不再需要存储实例变量beep了,他只是引用start方法中的beep参数变量。
这看起来好像没什么值得大惊小怪。程序行
if(beep)...
毕竟在start方法内部,但已经不能访问beep变量的值了。
为了清楚的看到内部问题,可以分析控制流程,
1)调用start方法。
2)调用内部类TimerPrinter构造器,以便初始化对象listener。
3)将listener引用传递给Timer构造器,定时器开始计时,start方法结束。此时,start方法的beep参数不复存在。
4)然后,actionPerformed方法执行if(beep)……。
所以可能会出现外部方法修改引用,而导致内部类得到的引用值不一致或者内部类修改引用,而导致外部方法的参数值在修改前和修改后不一致这种问题。我们一般在局部内部类中调用形参和方法中定义的变量,则这些变量需要加上final。
匿名内部类
将局部内部类的使用再深入一步。假如只创建这个类的一个对象,就不必命名,这种类被称为匿名内部类。
代码如下:
public void start (int interval, boolean beep){ ActionListener listener = new ActionListener(){ public void actionPerformed(ActionEvent event){ System.out.println("At the tone,the time is "+new Date()); if(beep) Toolkit.getDefaultToolkit().beep(); } }; Timer t= new Timer(interval,listener); t.start(); }
可以将这种语法理解为创建一个实现ActionListener接口的类的新对象,需要实现的方法在其定义的括号里。
对比匿名内部类和普通类之间的差别就是构造函数紧跟着一个大括号,正在定义的的就是匿名内部类
原文地址:https://www.cnblogs.com/zzuzhouxiang/p/10355726.html