1. 認識事件
在程式設計中,「事件」是表示某件事情已經發生的訊號。它可能是按下按鈕、輸入文字、資料載入完成、變數值改變——凡是在你的應用程式中可能發生、而你身為開發者想要回應的任何事。
打個比方,事件就像門鈴:有人來了,而你決定接下來要做什麼。你可以開門、忽略、假裝不在家,甚至叫全家來看是誰來了。在程式設計中,事件是「門鈴」,而你的反應就是事件處理器。
在 Java 中,事件是反應式與圖形介面(GUI)程式設計的基礎。沒有它們,就不會有按鈕、下拉選單、視窗應用程式,甚至許多伺服端系統。
監聽器 (Listener):是什麼、為什麼需要
監聽器(Listener)是訂閱特定事件並等待其發生的物件。一旦事件發生,監聽器就會收到訊號並執行預先定義的反應程式碼。
在 Java 中,監聽器通常透過介面來實作。最常見的例子之一是介面 ActionListener。需要對使用者動作(例如按下按鈕)做出反應的類別會去實作它。
在實務上,互動流程如下:有一個事件來源,例如按鈕;還有一個監聽器——實作了所需介面的物件。來源保存所有已訂閱它的監聽器清單。當使用者按下按鈕時,事件來源會針對每個監聽器呼叫特定的方法。
這就像訂閱新聞:只要你仍然訂閱,就會持續收到通知。在 Java 中,這個通知就是對監聽器方法的呼叫,而該方法會執行事先描述好的動作。
2. 事件介面:ActionListener、MouseListener 等
在 Java 中,對於每一種類型的事件,都有對應的監聽器介面:
| 事件類型 | 監聽器介面 | 使用場景 |
|---|---|---|
| 動作 | |
按鈕、選單、計時器 |
| 滑鼠 | |
能回應滑鼠的元件 |
| 鍵盤 | |
文字欄位、任意元件 |
| 變更 | |
滑桿、核取方塊、資料模型 |
| 文件 | |
文字變更(例如 JTextField) |
每個這樣的介面都定義了一個或多個必須實作的方法。比如 ActionListener 需要實作方法 actionPerformed(ActionEvent e)。
範例:ActionListener
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按鈕已被按下!");
}
}
3. 事件模型的工作原理
整個 Java 的事件模型建立在一個簡單卻強大的機制之上:
- 事件來源(例如按鈕)維護一個監聽器清單。
- 當發生某個動作(例如使用者點擊按鈕)時,來源會建立事件物件(例如 ActionEvent)。
- 來源遍歷所有已註冊的監聽器,呼叫它們對應的方法(例如 actionPerformed),並傳入事件物件。
- 每個監聽器自行決定如何處理該事件。
事件運作示意:
flowchart LR
A[使用者按下按鈕] --> B[按鈕建立事件]
B --> C[按鈕對所有監聽器呼叫 actionPerformed]
C --> D[監聽器響應:執行其程式碼]
程式碼範例:
import javax.swing.*;
public class EventDemo {
public static void main(String[] args) {
JButton button = new JButton("點我!");
button.addActionListener(new MyActionListener());
JFrame frame = new JFrame("事件範例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(button);
frame.setSize(200, 100);
frame.setVisible(true);
}
}
當使用者按下按鈕時,所有監聽器的 actionPerformed 方法都會被觸發。
4. 範例:為按鈕加入監聽器
我們用一個簡單的例子來說明經典流程。
步驟 1. 建立按鈕
JButton button = new JButton("說聲你好");
步驟 2. 建立監聽器
class HelloListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("哈囉,世界!");
}
}
步驟 3. 註冊監聽器
button.addActionListener(new HelloListener());
簡述:程式碼長這樣
JButton button = new JButton("說聲你好");
button.addActionListener(new HelloListener());
5. 匿名類別與 Lambda 表達式:簡潔又好用
匿名類別
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("匿名監聽器:嗨!");
}
});
Lambda 表達式(Java 8+)
button.addActionListener(e -> System.out.println("Lambda!嗨!"));
當你的事件反應只需要一兩行時,Lambda 是新增監聽器最精簡的方式。
6. 這與你的應用程式有何關聯?
把這個機制整合進你的學習用應用程式吧。比方說,我們有一個帶有「新增任務」按鈕的簡單視窗。當按下按鈕時,我們想把新任務加入清單並輸出一則訊息。
程式碼範例:
import javax.swing.*;
import java.awt.event.ActionListener;
public class TaskManagerGUI {
public static void main(String[] args) {
JFrame frame = new JFrame("任務管理器");
JButton addButton = new JButton("新增任務");
// 使用 lambda 作為監聽器
addButton.addActionListener(e -> {
System.out.println("任務已新增!");
// 這裡可以是把任務加入清單的邏輯
});
frame.add(addButton);
frame.setSize(300, 100);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
現在,你的應用程式不只是逐行執行,而是會「回應」使用者的操作!
7. 使用監聽器與事件時的常見錯誤
錯誤 1:忘了註冊監聽器。 如果你建立了監聽器,卻沒有把它加入事件來源,你的程式碼永遠不會被呼叫。這就像訂閱雜誌卻沒寄出申請——雜誌不會寄來。
錯誤 2:同一個監聽器被重複註冊。 如果你不小心多次加入同一個監聽器,處理器會被呼叫同樣的次數。有時候這有用,但更多時候會導致奇怪的錯誤(「為什麼我的函式觸發了三次?!」)。
錯誤 3:忘了移除監聽器。 如果監聽器已經不需要了,卻沒有移除,它仍會留在記憶體裡。在長時間運行的應用程式中,這可能導致記憶體洩漏。
錯誤 4:在事件處理器中執行耗時操作。 如果你在處理器中直接做很重的工作(例如從網際網路載入資料),介面會「卡住」,使用者會開始焦躁。較好的做法是把重活放到另一個執行緒。
錯誤 5:監聽器中的例外未處理。 如果在處理器裡發生例外,可能會「中斷」整個事件鏈。請記錄錯誤並妥善處理例外,避免應用程式崩潰。
GO TO FULL VERSION