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