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 构造的第一个参数是发出通告的时间间隔,他的单位是毫秒。第二个参数是监听器对象。

       最后,启动定时器:

t.start();

       在启动定时器后,程序将弹出一个消息对话框,并等待用户点击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();

              }

       };

}