关于Java的事件监听机制,我们首先需了解三个概念:
1.事件源(Event Source):即触发事件的对象,比如:Button对象可以修改按钮的状态,也就是说Button对象可以出发按钮按下这个事件。
2.事件状态对象(Event Object):描述事件的对象,对于一类特定的事件,我们需要记录事件发生时的一些状态,比如说事件触发的时间、按钮按下的次数(单击还是双击)、触发事件的对象(即事件源)、对事件的描述等等。我们需要一个专门描述这些状态的事件对象EventObject。
3.事件监听器(Event Listener):当我们打开QQ时,点击登陆按钮,立即显示出了好友列表的界面。即当登陆这个按钮被点击后(即点击事件被触发),程序执行了密码验证登陆QQ拉取好友列表等操作。那么,是哪些对象执行了这些操作呢?这些对象又是如何知道何时执行这些操作的呢?这便提出了事件监听的概念,对于在按钮点击后需要进行操作的那些对象,注册事件监听器(即实现ActionListener接口),并且实现actionPerformed(ActionEvent)方法,在这个方法中是对象在事件触发后具体的操作。JDK中的源码如下:
public interface ActionListener extends EventListener {
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e);
}
那么以上的三种对象是如何关联起来的呢?下面我们以Java的JButton类的实现描述一下三者的关系
在JDK源码中,我们可以发现,JButton继承AbstractButton类:
public class JButton extends AbstractButton implements Accessible
JButton类和AbstractButton类即我们上面提到的事件源
在AbstractButton类中,有一个方法addActionListener(ActionListener)
public void addActionListener(ActionListener l) {
listenerList.add(ActionListener.class, l);
}listenerList是AbstractButton类的一个成员变量
protected EventListenerList listenerList = new EventListenerList();
EventListenerList是一个比较简单的类,我们贴出这个类的源码如下:
public class EventListenerList implements Serializable {
/* A null array to be shared by all empty listener lists*/
private final static Object[] NULL_ARRAY = new Object[0]; /* The list of ListenerType - Listener pairs */
protected transient Object[] listenerList = NULL_ARRAY;
public Object[] getListenerList() {
return listenerList;
}
/**
* Return an array of all the listeners of the given type.
* @return all of the listeners of the specified type.
* @exception ClassCastException if the supplied class
* is not assignable to EventListener
*
* @since 1.3
*/
public <T extends EventListener> T[] getListeners(Class<T> t) {
Object[] lList = listenerList;
int n = getListenerCount(lList, t);
T[] result = (T[])Array.newInstance(t, n);
int j = 0;
for (int i = lList.length-2; i>=0; i-=2) {
if (lList[i] == t) {
result[j++] = (T)lList[i+1];
}
}
return result;
}
/**
* Returns the total number of listeners for this listener list.
*/
public int getListenerCount() {
return listenerList.length/2;
}
/**
* Returns the total number of listeners of the supplied type
* for this listener list.
*/
public int getListenerCount(Class<?> t) {
Object[] lList = listenerList;
return getListenerCount(lList, t);
}
private int getListenerCount(Object[] list, Class t) {
int count = 0;
for (int i = 0; i < list.length; i+=2) {
if (t == (Class)list[i])
count++;
}
return count;
}
/**
* Adds the listener as a listener of the specified type.
* @param t the type of the listener to be added
* @param l the listener to be added
*/
public synchronized <T extends EventListener> void add(Class<T> t, T l) {
if (l==null) {
// In an ideal world, we would do an assertion here
// to help developers know they are probably doing
// something wrong
return;
}
if (!t.isInstance(l)) {
throw new IllegalArgumentException("Listener " + l +
" is not of type " + t);
}
if (listenerList == NULL_ARRAY) {
// if this is the first listener added,
// initialize the lists
listenerList = new Object[] { t, l };
} else {
// Otherwise copy the array and add the new listener
int i = listenerList.length;
Object[] tmp = new Object[i+2];
System.arraycopy(listenerList, 0, tmp, 0, i);
tmp[i] = t;
tmp[i+1] = l;
listenerList = tmp;
}
}
/**
* Removes the listener as a listener of the specified type.
* @param t the type of the listener to be removed
* @param l the listener to be removed
*/
public synchronized <T extends EventListener> void remove(Class<T> t, T l) {
if (l ==null) {
// In an ideal world, we would do an assertion here
// to help developers know they are probably doing
// something wrong
return;
}
if (!t.isInstance(l)) {
throw new IllegalArgumentException("Listener " + l +
" is not of type " + t);
}
// Is l on the list?
int index = -1;
for (int i = listenerList.length-2; i>=0; i-=2) {
if ((listenerList[i]==t) && (listenerList[i+1].equals(l) == true)) {
index = i;
break;
}
}
// If so, remove it
if (index != -1) {
Object[] tmp = new Object[listenerList.length-2];
// Copy the list up to index
System.arraycopy(listenerList, 0, tmp, 0, index);
// Copy from two past the index, up to
// the end of tmp (which is two elements
// shorter than the old list)
if (index < tmp.length)
System.arraycopy(listenerList, index+2, tmp, index,
tmp.length - index);
// set the listener array to the new array or null
listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp;
}
}
// Serialization support.
private void writeObject(ObjectOutputStream s) throws IOException {
Object[] lList = listenerList;
s.defaultWriteObject();
// Save the non-null event listeners:
for (int i = 0; i < lList.length; i+=2) {
Class<?> t = (Class)lList[i];
EventListener l = (EventListener)lList[i+1];
if ((l!=null) && (l instanceof Serializable)) {
s.writeObject(t.getName());
s.writeObject(l);
}
}
s.writeObject(null);
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
listenerList = NULL_ARRAY;
s.defaultReadObject();
Object listenerTypeOrNull;
while (null != (listenerTypeOrNull = s.readObject())) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
EventListener l = (EventListener)s.readObject();
String name = (String) listenerTypeOrNull;
ReflectUtil.checkPackageAccess(name);
add((Class<EventListener>)Class.forName(name, true, cl), l);
}
}
/**
* Returns a string representation of the EventListenerList.
*/
public String toString() {
Object[] lList = listenerList;
String s = "EventListenerList: ";
s += lList.length/2 + " listeners: ";
for (int i = 0 ; i <= lList.length-2 ; i+=2) {
s += " type " + ((Class)lList[i]).getName();
s += " listener " + lList[i+1];
}
return s;
}
}
分析上面的源码我们可以发现,EventListenerList是一个存储Object对象的类,它的成员变量listenerList 就是一个Object对象数组,这个类主要用于操作这个对象数组,在AbstractButton类的addActionListener方法中,listenerList调用了add方法添加事件监听器。
由上面的分析可以看出,在事件源中添加事件监听器,每个等待事件触发需要进行操作的对象都实现了事件监听器接口,然后事件源对象调用addActionListener方法添加等待操作的事件触发器,一个事件源可以添加多个事件监听器。
当事件触发时,事件源对象会调用存在该对象中的listenerList中的每个监听器的actionPerformed的方法实现监听对象的操作。这便是Java的事件监听机制的部分原理。虽然源码读起来有些难度,但大家在读源码的时候关键是理解其中体现出的设计思想,一定不要仅仅拘泥于代码层面
有些内容限于本人技术水平有限,可能讲的比较啰嗦,如有不足读者可以提出,大家一起讨论。