當前位置:
首頁 > 知識 > 從零開始理解Java事件處理機制

從零開始理解Java事件處理機制

一:事件驅動模型初窺

我們要說事件驅動模型是觀察者模式的升級版本,那我們就要說說其中的對應關係:


觀察者對應監聽器(學生)

被觀察者對應事件源(教師)

事件源產生事件,事件帶有事件源,監聽器監聽事件。愛鑽牛角尖的朋友可能會說,我擦,什麼叫產生事件,監聽事件,事件事件到底什麼?

莫慌,如果我們用代碼來說事,事件它就是個類,事件源也是個類。這裡面一共牽扯到四個類,事件源(即教師、即被觀察者)、事件(是一個類,見下文,一般我們以Event或者EventObject命名結尾)、監聽器介面、具體的監聽器(即學生、即觀察者)。

就像我們上一篇文章中的第一節提到的一樣,JDK中當然有現成的事件模型類,我們不妨來一個一個的查看一下吧。

首先看監聽器(即學生、即觀察者,大家不要嫌我煩,不停滴括弧提醒,這是為了不停滴給大家加深印象),


package java.util;

/**

* A tagging interface that all event listener interfaces must extend.

* @since JDK1.1

*/

public interface EventListener {

}

簡單到不能再簡單了,對吧,甚至連一個聲明的方法都沒有,那它存在的意義在哪?還記得面向對象中的上溯造型嗎,所以它的意義就在於告訴所有的調用者,我是一個監聽器。

再來看看事件,即Event或EventObject結尾的那個類,裡面含有getSource方法,返回的就是事件源(即教師、即被觀察者),


package java.util;

/**

*

* The root class from which all event state objects shall be derived.

*

* All Events are constructed with a reference to the object, the "source",

* that is logically deemed to be the object upon which the Event in question

* initially occurred upon.

*

* @since JDK1.1

*/

public class EventObject implements java.io.Serializable {

private static final long serialVersionUID = 5516075349620653480L;

/**

* The object on which the Event initially occurred.

*/

protected transient Object source;

/**

* Constructs a prototypical Event.

*

* @param source The object on which the Event initially occurred.

* @exception IllegalArgumentException if source is null.

*/

public EventObject(Object source) {

if (source == null)

throw new IllegalArgumentException("null source");

this.source = source;

}

/**

* The object on which the Event initially occurred.

*

* @return The object on which the Event initially occurred.

*/

public Object getSource() {

return source;

}

/**

* Returns a String representation of this EventObject.

*

* @return A a String representation of this EventObject.

*/

public String toString() {

return getClass().getName() + "[source=" + source + "]";

}

}

這個類也很簡單,如果說觀察者模式中的上層類和結果還帶了不少邏輯不少方法的話,那麼事件驅動模型中的上層類和介面簡直看不到任何東西。沒錯,


事件驅動模型中,JDK的設計者們進行了最高級的抽象,就是讓上層類只是代表了:我是一個事件(含有事件源),或,我是一個監聽者!

二:老師布置作業的事件驅動模型版本

老規矩,讓我們先給出類圖:

從零開始理解Java事件處理機制

然後,代碼實現之:

觀察者介面(學生)。由於在事件驅動模型中,只有一個沒有任何方法的介面,EventListener,所以,我們可以先實現一個自己的介面。為了跟上一篇的代碼保持一致,在該介面中我們聲明的方法的名字也叫update。注意,我們當然也可以不取這個名字,甚至還可以增加其它的方法聲明,全看我們的業務需要。


package com.zuikc.events;

import java.util.Observable;

public interface HomeworkListener extends java.util.EventListener {

public void update(HomeworkEventObject o, Object arg);

}

繼而實現觀察者類(學生),如下:

package com.zuikc.events;

public class Student implements HomeworkListener{

private String name;

public Student(String name){

this.name = name;

}

@Override

public void update(HomeworkEventObject o, Object arg) {

Teacher teacher = o.getTeacher();

System.out.printf("學生%s觀察到(實際是被通知)%s布置了作業《%s》
", this.name, teacher.getName(), arg);

}

}

對比一下上篇,有變動沒?

繼而實現事件子類,如下:


package com.zuikc.events;

public class HomeworkEventObject extends java.util.EventObject {

public HomeworkEventObject(Object source) {

super(source);

}

public HomeworkEventObject(Teacher teacher) {

super(teacher);

}

public Teacher getTeacher(){

return (Teacher) super.getSource();

}

}

在這個類中,指的關注的就是這個getTeacher方法,它封裝了父類EventObject的getSource方法,得到了事件源。理論上,我們使用父類的getSource方法也可行,但是重新在子類封裝一下,可讀性更強一點。

然後呢,然後就是我們的教師類,教師類就是事件源,如下:


package com.zuikc.events;

import java.util.*;

public class Teacher {

private String name;

private List homeworks;

/*

* 教師類要維護一個自己監聽器(學生)的列表,為什麼?

* 在觀察者模式中,教師是被觀察者,繼承自java.util.Observable,Observable中含了這個列表

* 現在我們沒有這個列表了,所以要自己創建一個

*/

private Set homeworkListenerList;

public String getName() {

return this.name;

}

public Teacher(String name) {

this.name = name;

this.homeworks = new ArrayList();

this.homeworkListenerList = new HashSet();

}

public void setHomework(String homework) {

System.out.printf("%s布置了作業%s
", this.name, homework);

homeworks.add(homework);

HomeworkEventObject event = new HomeworkEventObject(this);

/*

* 在觀察者模式中,我們直接調用Observable的notifyObservers來通知被觀察者

* 現在我們只能自己通知了~~

*/

for (HomeworkListener listener : homeworkListenerList) {

listener.update(event, homework);

}

}

public void addObserver(HomeworkListener homeworkListener){

homeworkListenerList.add(homeworkListener);

}

}

這個類稍微長了那麼一點點,有幾個地方值得注意:


第一處地方,Teacher沒有父類了,Teacher作為事件中的事件源Source被封裝到HomeworkEventObject中了。這沒有什麼不好的,業務對象和框架代碼隔離開來,解耦的非常好,但是正因為如此,我們需要在Teacher中自己維護一個Student的列表,於是,我們看到了homeworkListenerList這個變數。

第二處,在觀察者模式中,我們直接調用Observable的notifyObservers來通知被觀察者,現在我們只能靠自己了,於是我們看到了這段代碼,

for (HomeworkListener listener : homeworkListenerList) {

listener.update(event, homework);

}

這一點問題也沒有,我們繼續來看客戶端代碼吧:


package com.zuikc.events;

import java.util.EventListener;

public class Client {

public static void main(String[] args) {

Student student1= new Student("張三");

Student student2 = new Student("李四");

Teacher teacher1 = new Teacher("zuikc");

teacher1.addObserver(student1);

teacher1.addObserver(student2);

teacher1.setHomework("事件機制第二天作業");

}

}

結果如下:

從零開始理解Java事件處理機制

從客戶端的角度來說,我們幾乎完全沒有更改任何地方,跟觀察者模式的客戶端代碼一模一樣,但是內部的實現機制上,我們卻使用了事件機制。

現在我們來總結下,觀察者模式和事件驅動模型的幾個不同點:

1:事件源不再繼承任何模式或者模型本身的父類,徹底將業務代碼解耦出來;

2:在事件模型中,每個監聽者(觀察者)都需要實現一個自己的介面。沒錯,看看我們的滑鼠事件,分表就有單擊、雙擊、移動等等的事件,這分別就是增加了代碼的靈活性;

不管怎麼說,我們用一堆小白代碼實現了一個事件驅動模型的樣例,雖然沒什麼實際用處,但也解釋了原理,接下來的一節,我們就要看看那些稍微複雜且看上去很有用的代碼了!


學習Java的同學注意了!!!

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入Java學習交流群495273252,我們一起學Java!

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Java團長 的精彩文章:

Java對字元串進行的操作
讀《深入理解Java虛擬機》
程序員如何對待自己的工作
架構師應該是一種角色——而不是一個職位

TAG:Java團長 |

您可能感興趣

在shiro中內置登錄管理器如何進行驗證碼處理
Perl 錯誤處理
英國Sellafield核處理站與Createc合作,通過VR控制機器人進行核廢料處理
Python文件處理
開源服務容錯處理庫Polly使用文檔
批處理ETL已死,Kafka才是數據處理的未來?
後期處理:Camera Raw基礎教程
常用cookie處理方法工具類
Sony 與微軟下一代遊戲機處理器將採用 AMD Ryzen 處理器
Facebook研發AI處理工具,可挽救拍照時閉眼難題
Servlet 異常處理
使用Camtasia軟體處理視頻素材
Android非同步處理
未來Windows設備配有專門的AI處理器
阿布扎比:金融服務管理局正在處理Cryptocurrency和ICO法規
Intel處理器漏洞修補完畢!未來處理器徹底免疫
Moto新機很尷尬 聯想副總裁說處理器是個垃圾
蘋果Mac將自研處理器 運行iPhone應用
使用shell命令循環處理文件
處理JPG,也彈出Camera raw 界面,怎麼辦?