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