Java初学者之——内部类的应用
首先,我们来看一下什么是内部类?
内部类(inner class)是定义在另一个类中的类。
那么为什么需要使用内部类呢?
其主要原因有以下三点:
(1) 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
(2) 内部类可以对同一个包中的其他类隐藏起来。
(3) 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
什么是回调呢?回调(callback),是一种常见的程序设计模式。在这种模式中,可以指出某个特定事件发生时应该采取的行动。例如,指出在鼠标或选择某个菜单项时应该采取什么行动。
在学习内部类之前,我们来看一个例子程序,在本节学习内部类的工程中,都将围绕着这个例子程序学扩展。
在java.swing包中,有一个Timer类,可以使用它在到达给定的时间间隔时发出通告。例如,假如程序中有一个时钟,那么就可以请求每秒钟获得一个通告,以便更新时钟的画面。
在构造定时器时,需要设置一个时间间隔,并告知定时器,当到达时间间隔时需要做些什么操作。
如何告知定时期做什么呢?在很多程序设计语言中,可以提供一个函数名,定时器周期性的调用它。但是,在Java标准类库中类采用的是面向对象方法。它将某个类的对象传递给定时器,然后,定时器调用这个对象的方法。由于对象可以携带一些附加的信息,所以传递一个对象比传递一个函数要灵活的多。
当然,定时器需要知道调用哪一个方法,并要求传递的对象所属的类实现了java.awt.event包的ActionListener接口。下面是这个接口:
public interface ActionListener
{
void actionPerformed(ActionEvent event);
}
|
当到达指定的时间间隔时,定时器就调用actionPerformer方法。假设希望每隔10秒钟,打印一条信息“At the tone , the time is …” ,然后响一声(beep),就应该定义一个实现ActionListener接口的类,然后将需要执行的语句放在actionPerformed方法中。
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now =new Date();
System.out.println(“At the tone , the time is ” + now );
Toolkit.getDefaltToolkit().beep();
}
}
|
需要注意actionPerformed方法的ActionEvent 参数。这个参数提供了时间的相关信息。例如,产生这个事件的源对象。
接下来,构造这个类的一个对象,并将它传递给Timer构造器。
ActionListener listener = new TimePrinter();
Timer t =new Timer (10000 , listener);
|
Timer 构造的第一个参数是发出通告的时间间隔,他的单位是毫秒。第二个参数是监听器对象。
最后,启动定时器:
在启动定时器后,程序将弹出一个消息对话框,并等待用户点击OK按钮来终止程序的执行。在程序等待用户操作的同时,每隔10秒显示一次当前的时间。
需要注意:这个程序除了导入javax.swing.* 和java.util.* 外,还通过类名导入了javax.swing.Timer 。这就消除了javax.swing.Timer 与 java.util.Timer 之间产生的二义性。这里的java.util.Timer 是一个与本例无关的类,它主要用于调度后台任务。
程序全部代码如下:
import java.util.* ;
import java.awt.* ;
import java.awt.event.* ;
import javax.swing.* ;
import javax.swing.Timer ;
public class TimerTest
{
public static void main(String [] args)
{
ActionListener listener=new TimerPrinter();
Timer t = new Timer(10000, listener) ;
t.start( ) ;
JOptionPane.showMessageDialog(null , "Quit program?");
System.exit(0);
}
}
class TimerPrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("At the tone , the time is " + now);
Toolkit.getDefaultToolkit().beep() ;
}
}
|
一、使用内部类访问对象状态
内部类的语法比较复杂。鉴于此,我们选择一个简单但不太适用的例子说明内部类的是用方式。下面将近一部分析TimerTest例子,并抽象出一个TalkingClock类。构造一个语音时钟时需要提供两个参数:发布通告的间隔和开关铃音的标志。
class TalkingClock
{
public TalkingClock(int interval,boolean beep)
{
.....;
}
public void start()
{
......;
}
private int interval;
private boolean beep;
private class TimerPrinter implements ActionListener
{
.....;
};
}
|
需要注意,这里的TimerPrinter类位于TalkingClock 类内部。这并不意味着每个TalkingClock 都有一个TimerPrinter 实例域。如前所示,TimePrinter对象是由TalkingClock的方法构造。
TimerPrinter类中有一个私有内部类(private inner class)。这是一种安全机制。这样一来,只有TalkingClock类中的方法才能够生成TimePrinter对象。
只有内部类可以是私有类,而常规类只能具有包的可见性,或共有的可见性。
下面是TimePrinter 类的详细内容。需要注意一点,actionPerformed方法在发出铃声之前检查了beep标志。
private class TimerPrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now=new Date();
System.out.println("At the tone, the time is "+ now );
if(beep)
Toolkit.getDefaultToolkit().beep();
}
};
|
为了能够运行这个程序,内部类的对象要有一个隐式引用,它指向了创建它的外围类对象。这个引用在内部类的定义中是不可见的。然而,为了说明这个概念,我们将外围类对象的引用称为outer。于是actionPerformed方法将等价于下列形式:
private class TimerPrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now=new Date();
System.out.println("At the tone, the time is "+ now );
if(outer.beep)
Toolkit.getDefaultToolkit().beep();
}
};
|
下面给出一个测试内部类的完成程序:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
public class InnerClassTest
{
public static void main(String [] args)
{
TalkingClock clock= new TalkingClock(1000,true);
clock.start();
JOptionPane.showMessageDialog(null,"Quit program?");
System.exit(0);
}
};
class TalkingClock
{
public TalkingClock(int interval,boolean beep)
{
this.interval=interval;
this.beep=beep;
}
public void start()
{
ActionListener listener =new TimerPrinter();
Timer t=new Timer(interval, listener);
t.start();
}
private int interval;
private boolean beep;
private class TimerPrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now=new Date();
System.out.println("At the tone, the time is "+ now );
if(beep)
Toolkit.getDefaultToolkit().beep();
}
};
}
|