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();

              }

       };

}