首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 事件響應機制完整匯總:四種核心實作模式

     編輯:Java 程式設計入門

事件處理四步驟

  1. 建立接收響應的組件(控件)
  2. 實作相關事件監聽介面
  3. 註冊事件源的動作監聽器
  4. 事件觸發時的事件處理

四種核心實作模式

模式一:容器類監聽

視窗/面板類別直接實作 ActionListener,this 即為監聽器。


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class ButtonListener extends JFrame implements ActionListener {
    JButton ok, cancel, exit;

    public ButtonListener(String title) {
        super(title);
        setLayout(new FlowLayout());
        
        ok = new JButton("確定");
        cancel = new JButton("返回");
        exit = new JButton("退出");
        
        // 註冊監聽器
        ok.addActionListener(this);
        cancel.addActionListener(this);
        exit.addActionListener(this);
        
        add(ok); add(cancel); add(exit);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == ok) System.out.println("確定");
        else if (e.getSource() == cancel) System.out.println("返回");
        else if (e.getSource() == exit) System.exit(0);
    }

    public static void main(String[] args) {
        ButtonListener f = new ButtonListener("ActionEvent Demo");
        f.setSize(250, 100);
        f.setVisible(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}
  • 優點: 程式碼集中、容易理解
  • 缺點: 類別膨脹、違反單一職責、難以複用邏輯

模式二:獨立監聽器類別

建立專門的監聽器類別實作 ActionListener,元件註冊其實例。


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class ButtonListener1 extends JFrame {
    JButton ok, cancel, exit;

    public ButtonListener1(String title) {
        super(title);
        setLayout(new FlowLayout());
        
        ok = new JButton("確定");
        cancel = new JButton("返回");
        exit = new JButton("退出");
        
        // 共用同一監聽器實例
        ActionListener listener = new MyListener();
        ok.addActionListener(listener);
        cancel.addActionListener(listener);
        exit.addActionListener(listener);
        
        add(ok); add(cancel); add(exit);
    }

    public static void main(String[] args) {
        new ButtonListener1("ActionEvent Demo").setVisible(true);
    }
}

class MyListener implements ActionListener {
    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        if ("確定".equals(cmd)) System.out.println("確定");
        else if ("返回".equals(cmd)) System.out.println("返回");
        else if ("退出".equals(cmd)) System.exit(0);
    }
}
  • 優點: 關注點分離、監聽器可複用、符合單一職責
  • 缺點: 需傳遞外部參考以存取 UI 狀態

模式三:AbstractAction 實作

AbstractAction 同時實作 ActionListener 與 Action 介面,封裝動作屬性(名稱、圖示、快捷鍵、啟用狀態)。


import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;

class AbstractEvent extends AbstractAction {
    @Override
    public void actionPerformed(ActionEvent e) {
        String cmd = e.getActionCommand();
        switch (cmd) {
            case "open":  JOptionPane.showMessageDialog(null, "打開"); break;
            case "close": JOptionPane.showMessageDialog(null, "關閉"); break;
            case "run":   JOptionPane.showMessageDialog(null, "運行"); break;
            case "stop":  JOptionPane.showMessageDialog(null, "停止"); break;
        }
    }
}

public class TestAbstractEvent {
    private static JMenuBar menubar;
    private static JFrame frame;
    private static Action menuAction = new AbstractEvent();

    public TestAbstractEvent() {
        frame = new JFrame("Menu Demo");
        frame.setLayout(new BorderLayout());
        
        menubar = new JMenuBar();
        
        JMenu menuFile = new JMenu("File");
        menuFile.add(createMenuItem("open", "開啟"));
        menuFile.add(createMenuItem("close", "關閉"));
        
        JMenu menuTool = new JMenu("Tool");
        menuTool.add(createMenuItem("run", "執行"));
        menuTool.add(createMenuItem("stop", "停止"));
        
        menubar.add(menuFile);
        menubar.add(menuTool);
        frame.add(menubar, BorderLayout.NORTH);
        
        frame.setSize(400, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
    
    private JMenuItem createMenuItem(String actionCommand, String text) {
        JMenuItem item = new JMenuItem(text);
        item.setActionCommand(actionCommand);
        item.addActionListener(menuAction);
        return item;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestAbstractEvent::new);
    }
}
  • 優點: 封裝動作屬性、支援選單/工具列/按鈕共用、啟用狀態集中控制
  • 缺點: 較複雜、需理解 Action 架構

模式四:AbstractAction + 反射

按鈕名稱對應處理類別,透過反射動態建立實例執行。


import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;

class ViewAction extends AbstractAction {
    private String actionName;
    private Action action;  // 快取實例

    public ViewAction(String actionName) { this.actionName = actionName; }

    @Override
    public void actionPerformed(ActionEvent e) {
        Action a = getAction(actionName);
        if (a != null) a.execute();
    }

    private Action getAction(String name) {
        if (action != null) return action;
        try {
            action = (Action) Class.forName(name).newInstance();
            return action;
        } catch (Exception ex) { return null; }
    }
}

interface Action { void execute(); }

class b1 implements Action { public void execute() { JOptionPane.showMessageDialog(null, "點擊了 b1"); } }
class b2 implements Action { public void execute() { JOptionPane.showMessageDialog(null, "點擊了 b2"); } }
class b3 implements Action { public void execute() { JOptionPane.showMessageDialog(null, "點擊了 b3"); } }

public class TestAE extends JFrame {
    public TestAE() {
        super("反射事件Demo");
        JToolBar bar = new JToolBar();
        String[] btnNames = {"b1", "b2", "b3"};
        
        for (String name : btnNames) {
            ViewAction action = new ViewAction(name);
            JButton btn = new JButton(name);
            btn.addActionListener(action);
            bar.add(btn);
        }
        
        add(bar, BorderLayout.NORTH);
        setSize(300, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(TestAE::new);
    }
}
  • 優點: 高度解耦、動態擴展、配置驅動
  • 缺點: 反射效能、編譯期無檢查、除錯困難

進階事件處理技巧

1. Lambda 表達式


button.addActionListener(e -> System.out.println("Clicked!"));
// 或方法引用
button.addActionListener(this::handleClick);

2. 事件適配器類別


// 避免實作介面所有方法
window.addWindowListener(new WindowAdapter() {
    @Override public void windowClosing(WindowEvent e) { System.exit(0); }
});

3. 事件總線


// Guava EventBus / Spring ApplicationEventPublisher
@EventListener
public void handleOrderEvent(OrderCreatedEvent event) { ... }

4. AWT 事件佇列與 EDT

  • 所有 Swing 事件在 Event Dispatch Thread (EDT) 處理
  • 長時間運算需用 SwingWorker 或 ExecutorService 避免凍結 UI
  • SwingUtilities.invokeLater() 將任務排入 EDT

模式選擇指南

場景推薦模式理由
簡單視窗、少量按鈕模式一(容器監聽)程式碼集中、易寫易讀
中型應用、邏輯複用模式二(獨立監聽器)關注點分離、可測試
選單/工具列/按鈕共用動作模式三Action 封裝屬性、狀態同步
插件架構、動態載入模式四反射解耦、配置驅動
現代 Java (8+)Lambda + 方法引用簡潔、函數式

常見陷阱

問題原因解決
事件不觸發未註冊監聽器、組件不可見/不可用檢查 addActionListener、setEnabled(true)、setVisible(true)
重複觸發重複註冊同一監聽器註冊前移除舊監聽器、或使用標記防重
UI 凍結EDT 執行耗時操作SwingWorker、CompletableFuture、ExecutorService
記憶體洩漏監聽器持有外部引用未移除移除監聽器、使用 WeakReference
反射找不到類類名錯誤、ClassLoader 問題、未編譯使用完整類名、確認 classpath、捕獲異常日誌

結語

Java 事件機制核心是觀察者模式:事件源 + 監聽器 + 事件物件。從簡單的容器監聽到 AbstractAction 封裝、反射動態分發,各模式權衡了簡潔性、複用性、解耦度、效能。現代開發推薦Lambda/方法引用處理簡單邏輯,Action 架構管理複雜工具列/選單,事件總線實現跨組件通訊。理解 EDT 執行模型、避免記憶體洩漏,是構建健壯 Swing/JavaFX 應用的關鍵。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved