观察者模式(参考《java与设计模式》

为什么要有观察者模式?

软件系统中,在某些场景中需要实现一个对象状态发生变化时,与之相关的对象也需要对应的发生改变。

观察者模式如何实现

简单来说通过就是一个Subject类通过一个能够存储多个Observer类的容器来保存对,这些类的应用,当Subject

类状态发生改变时,通知保存应用的Observer调用自身的更新方法。因为Observer在实际应用中不可能是同一个类

因此需要定义一个Observer,相关类继承实现即可。同理因为被观察者大部分操作是相同的,因此又定义一个Subject接口然后将共通的attach,detach,notifyObserver等操作在这里定义,需要观察者模式的类继承实现即可。

类图

第一种:


第二种


代码

第一种

package com.company;

public interface Subject {
    public void attach(Observer observer);
    public void detach(Observer observer);
    void notifyObservers();
}
package com.company;

public interface Observer {
    public void update();
}
package com.company;

import java.util.Iterator;
import java.util.Vector;

public class ConcreteSubject implements Subject{
    private Vector<Observer> observersVector=new Vector<Observer>();
    @Override
    public void attach(Observer observer) {
        observersVector.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        observersVector.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for(Iterator<Observer> iterator=observersVector.iterator();iterator.hasNext();iterator.next())
        {
            ((Observer)iterator).update();
        }
    }
}
package com.company;

public class ConcreteObserver implements Observer {

    @Override
    public void update() {
        System.out.println("I am notified");
    }
}

第二种

接口改成类

package com.company;

import java.util.Iterator;
import java.util.Vector;

public class Subject {
    private Vector<Observer> observersVector=new Vector<Observer>();
    public void attach(Observer observer) {
        observersVector.add(observer);
    }

    public void detach(Observer observer) {
        observersVector.remove(observer);
    }

    public void notifyObservers() {
        for(Iterator<Observer> iterator = observersVector.iterator(); iterator.hasNext(); iterator.next())
        {
            ((Observer)iterator).update();
        }
    }
}

package com.company;

public interface Observer {
    public void update();
}

package com.company;

import java.util.Iterator;
import java.util.Vector;

public class ConcreteSubject extends Subject{
   private  String state;
   public void change(String newState)
   {
       state=newState;
       this.notifyObservers();
   }
}
package com.company;

public class ConcreteObserver implements Observer {

    @Override
    public void update() {
        System.out.println("I am notified");
    }
}

这两种实现有和区别?

它们之间的区别就是将管理Observer聚集的实现到底是交由Subject实现还是ConcreteSubject,我们根据实际情况思考下,在大部分情况下,不同场景它们管理Observer聚集的方法是类似的,只是何时调用更新方法不同。因此第二种比第一种更加合理。



例子

java对观察者模式的支持

观察者实现Observer接口,被观察者实现Observerable,它们采用的也是上面第二种实现方式

package java.util;

/**
 * A class can implement the <code>Observer</code> interface when it
 * wants to be informed of changes in observable objects.
 *
 * @author  Chris Warth
 * @see     java.util.Observable
 * @since   JDK1.0
 */
public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}


package java.util;

/**
 * This class represents an observable object, or "data"
 * in the model-view paradigm. It can be subclassed to represent an
 * object that the application wants to have observed.
 * <p>
 * An observable object can have one or more observers. An observer
 * may be any object that implements interface <tt>Observer</tt>. After an
 * observable instance changes, an application calling the
 * <code>Observable</code>'s <code>notifyObservers</code> method
 * causes all of its observers to be notified of the change by a call
 * to their <code>update</code> method.
 * <p>
 * The order in which notifications will be delivered is unspecified.
 * The default implementation provided in the Observable class will
 * notify Observers in the order in which they registered interest, but
 * subclasses may change this order, use no guaranteed order, deliver
 * notifications on separate threads, or may guarantee that their
 * subclass follows this order, as they choose.
 * <p>
 * Note that this notification mechanism has nothing to do with threads
 * and is completely separate from the <tt>wait</tt> and <tt>notify</tt>
 * mechanism of class <tt>Object</tt>.
 * <p>
 * When an observable object is newly created, its set of observers is
 * empty. Two observers are considered the same if and only if the
 * <tt>equals</tt> method returns true for them.
 *
 * @author  Chris Warth
 * @see     java.util.Observable#notifyObservers()
 * @see     java.util.Observable#notifyObservers(java.lang.Object)
 * @see     java.util.Observer
 * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
 * @since   JDK1.0
 */
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    /** Construct an Observable with zero Observers. */

    public Observable() {
        obs = new Vector<>();
    }

    /**
     * Adds an observer to the set of observers for this object, provided
     * that it is not the same as some observer already in the set.
     * The order in which notifications will be delivered to multiple
     * observers is not specified. See the class comment.
     *
     * @param   o   an observer to be added.
     * @throws NullPointerException   if the parameter o is null.
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * Deletes an observer from the set of observers of this object.
     * Passing <CODE>null</CODE> to this method will have no effect.
     * @param   o   the observer to be deleted.
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to
     * indicate that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and <code>null</code>. In other
     * words, this method is equivalent to:
     * <blockquote><tt>
     * notifyObservers(null)</tt></blockquote>
     *
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * If this object has changed, as indicated by the
     * <code>hasChanged</code> method, then notify all of its observers
     * and then call the <code>clearChanged</code> method to indicate
     * that this object has no longer changed.
     * <p>
     * Each observer has its <code>update</code> method called with two
     * arguments: this observable object and the <code>arg</code> argument.
     *
     * @param   arg   any object.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#hasChanged()
     * @see     java.util.Observer#update(java.util.Observable, java.lang.Object)
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    /**
     * Clears the observer list so that this object no longer has any observers.
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * Indicates that this object has no longer changed, or that it has
     * already notified all of its observers of its most recent change,
     * so that the <tt>hasChanged</tt> method will now return <tt>false</tt>.
     * This method is called automatically by the
     * <code>notifyObservers</code> methods.
     *
     * @see     java.util.Observable#notifyObservers()
     * @see     java.util.Observable#notifyObservers(java.lang.Object)
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * Tests if this object has changed.
     *
     * @return  <code>true</code> if and only if the <code>setChanged</code>
     *          method has been called more recently than the
     *          <code>clearChanged</code> method on this object;
     *          <code>false</code> otherwise.
     * @see     java.util.Observable#clearChanged()
     * @see     java.util.Observable#setChanged()
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * Returns the number of observers of this <tt>Observable</tt> object.
     *
     * @return  the number of observers of this object.
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

在下面这个方法中

public void notifyObservers(Object arg) {
    /*
     * a temporary array buffer, used as a snapshot of the state of
     * current Observers.
     */
    Object[] arrLocal;

    synchronized (this) {
        /* We don't want the Observer doing callbacks into
         * arbitrary code while holding its own Monitor.
         * The code where we extract each Observable from
         * the Vector and store the state of the Observer
         * needs synchronization, but notifying observers
         * does not (should not).  The worst result of any
         * potential race-condition here is that:
         * 1) a newly-added Observer will miss a
         *   notification in progress
         * 2) a recently unregistered Observer will be
         *   wrongly notified when it doesn't care
         */
        if (!changed)
            return;
        arrLocal = obs.toArray();
        clearChanged();
    }

    for (int i = arrLocal.length-1; i>=0; i--)
        ((Observer)arrLocal[i]).update(this, arg);
}

可以看出来如果想要成功通知注册的观察者,changed这个变量必须是true才可以,因此每次调用notifyObservers之前必须调用setChanged

 */
protected synchronized void setChanged() {
    changed = true;
}

使用Observer和Observable的例子

生成一个MyObservable即完成注册操作

package com.company;

import java.util.Observable;

public class MyObservable extends Observable {
    private String data="";
    public String getData(){
        return data;
    }
    public void changeData(String data)
    {
        if(!this.data.equals(data)){
            this.data=data;
            setChanged();
        }
        notifyObservers();
    }
}
package com.company;

import java.util.Observable;
import java.util.Observer;

public class MyObserver implements Observer {

    public MyObserver(MyObservable observable){
        observable.addObserver(this);
    }
    @Override
    public void update(Observable o, Object arg) {
        System.out.println("Data:"+((MyObservable)o).getData());
    }
}
package com.company;

public class Main {
    static private MyObservable observable;
    static private MyObserver observer;
    public static void main(String[] args) {
   // write your code here
        observable=new MyObservable();
        observer=new MyObserver(observable);
        observable.changeData("1");
        observable.changeData("2");
        observable.changeData("3");
    }
}


结果


在java中的应用

下面Servlet和AWT个都是DEM(委派事件模型)的应用,DEM是基于观察者模式的

Servlet

Servlet与Session的打开关闭和修改属性都有事件接口,接口就是Observr,我们继承实现的则是ConcreteObserver,通过addXXXX函数则相当于被观察者注册观察者。但在Servlet中一般采用注册加入如下

<listener>
     <listener-class>cn.itcast.web.listener.MyServletContextListener </listener-class> 
</listener>
将自己继承接口的类在配置文件注册一下

常见事件接口



源码如下

Servlet

package javax.servlet;

import java.util.EventListener;

public interface ServletContextListener extends EventListener {
    void contextInitialized(ServletContextEvent var1);

    void contextDestroyed(ServletContextEvent var1);
}
package javax.servlet;

import java.util.EventListener;

public interface ServletContextAttributeListener extends EventListener {
    void attributeAdded(ServletContextAttributeEvent var1);

    void attributeRemoved(ServletContextAttributeEvent var1);

    void attributeReplaced(ServletContextAttributeEvent var1);
}

Session

package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionListener extends EventListener {
    void sessionCreated(HttpSessionEvent var1);

    void sessionDestroyed(HttpSessionEvent var1);
}
package javax.servlet.http;

import java.util.EventListener;

public interface HttpSessionAttributeListener extends EventListener {
    void attributeAdded(HttpSessionBindingEvent var1);

    void attributeRemoved(HttpSessionBindingEvent var1);

    void attributeReplaced(HttpSessionBindingEvent var1);
}


AWT

AWT事件继承关系

ps AWT1.0采用责任链模式,最上面类是Event,后来修改诚DEM模式事件类最上面是EventObject


上图左右两边的继承链分开是有原因的因为

AWT事件模型中分为语义事件和底层事件

语义事件一般指的是用户所做的事情



在AWT里不同的容器就是ConcreteSubject用来注册各种个样的事件然后当ConcreteSubject发生变化时通知注册的事件监听器来调用对应的方法。




Swing

javax.swing.Timer里面的Timer类使用了观察者模式

查看源码我们可以他有一个EventListener来保存注册的监听器

addActionListener和removeActionListener来注册和取消

在fireActionPerformed方法里通知所有注册的监听器调用它们自己的actionPerformed

package javax.swing;



import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.*;
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
import java.io.*;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import javax.swing.event.EventListenerList;



/**
 * Fires one or more {@code ActionEvent}s at specified
 * intervals. An example use is an animation object that uses a
 * <code>Timer</code> as the trigger for drawing its frames.
 *<p>
 * Setting up a timer
 * involves creating a <code>Timer</code> object,
 * registering one or more action listeners on it,
 * and starting the timer using
 * the <code>start</code> method.
 * For example,
 * the following code creates and starts a timer
 * that fires an action event once per second
 * (as specified by the first argument to the <code>Timer</code> constructor).
 * The second argument to the <code>Timer</code> constructor
 * specifies a listener to receive the timer's action events.
 *
 *<pre>
 *  int delay = 1000; //milliseconds
 *  ActionListener taskPerformer = new ActionListener() {
 *      public void actionPerformed(ActionEvent evt) {
 *          <em>//...Perform a task...</em>
 *      }
 *  };
 *  new Timer(delay, taskPerformer).start();</pre>
 *
 * <p>
 * {@code Timers} are constructed by specifying both a delay parameter
 * and an {@code ActionListener}. The delay parameter is used
 * to set both the initial delay and the delay between event
 * firing, in milliseconds. Once the timer has been started,
 * it waits for the initial delay before firing its
 * first <code>ActionEvent</code> to registered listeners.
 * After this first event, it continues to fire events
 * every time the between-event delay has elapsed, until it
 * is stopped.
 * <p>
 * After construction, the initial delay and the between-event
 * delay can be changed independently, and additional
 * <code>ActionListeners</code> may be added.
 * <p>
 * If you want the timer to fire only the first time and then stop,
 * invoke <code>setRepeats(false)</code> on the timer.
 * <p>
 * Although all <code>Timer</code>s perform their waiting
 * using a single, shared thread
 * (created by the first <code>Timer</code> object that executes),
 * the action event handlers for <code>Timer</code>s
 * execute on another thread -- the event-dispatching thread.
 * This means that the action handlers for <code>Timer</code>s
 * can safely perform operations on Swing components.
 * However, it also means that the handlers must execute quickly
 * to keep the GUI responsive.
 *
 * <p>
 * In v 1.3, another <code>Timer</code> class was added
 * to the Java platform: <code>java.util.Timer</code>.
 * Both it and <code>javax.swing.Timer</code>
 * provide the same basic functionality,
 * but <code>java.util.Timer</code>
 * is more general and has more features.
 * The <code>javax.swing.Timer</code> has two features
 * that can make it a little easier to use with GUIs.
 * First, its event handling metaphor is familiar to GUI programmers
 * and can make dealing with the event-dispatching thread
 * a bit simpler.
 * Second, its
 * automatic thread sharing means that you don't have to
 * take special steps to avoid spawning
 * too many threads.
 * Instead, your timer uses the same thread
 * used to make cursors blink,
 * tool tips appear,
 * and so on.
 *
 * <p>
 * You can find further documentation
 * and several examples of using timers by visiting
 * <a href="https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html"
 * target = "_top">How to Use Timers</a>,
 * a section in <em>The Java Tutorial.</em>
 * For more examples and help in choosing between
 * this <code>Timer</code> class and
 * <code>java.util.Timer</code>,
 * see
 * <a href="http://java.sun.com/products/jfc/tsc/articles/timer/"
 * target="_top">Using Timers in Swing Applications</a>,
 * an article in <em>The Swing Connection.</em>
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans&trade;
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @see java.util.Timer <code>java.util.Timer</code>
 *
 *
 * @author Dave Moore
 */
@SuppressWarnings("serial")
public class Timer implements Serializable
{
    /*
     * NOTE: all fields need to be handled in readResolve
     */

    protected EventListenerList listenerList = new EventListenerList();

    // The following field strives to maintain the following:
    //    If coalesce is true, only allow one Runnable to be queued on the
    //    EventQueue and be pending (ie in the process of notifying the
    //    ActionListener). If we didn't do this it would allow for a
    //    situation where the app is taking too long to process the
    //    actionPerformed, and thus we'ld end up queing a bunch of Runnables
    //    and the app would never return: not good. This of course implies
    //    you can get dropped events, but such is life.
    // notify is used to indicate if the ActionListener can be notified, when
    // the Runnable is processed if this is true it will notify the listeners.
    // notify is set to true when the Timer fires and the Runnable is queued.
    // It will be set to false after notifying the listeners (if coalesce is
    // true) or if the developer invokes stop.
    private transient final AtomicBoolean notify = new AtomicBoolean(false);

    private volatile int     initialDelay, delay;
    private volatile boolean repeats = true, coalesce = true;

    private transient final Runnable doPostEvent;

    private static volatile boolean logTimers;

    private transient final Lock lock = new ReentrantLock();

    // This field is maintained by TimerQueue.
    // eventQueued can also be reset by the TimerQueue, but will only ever
    // happen in applet case when TimerQueues thread is destroyed.
    // access to this field is synchronized on getLock() lock.
    transient TimerQueue.DelayedTimer delayedTimer = null;

    private volatile String actionCommand;

    /**
     * Creates a {@code Timer} and initializes both the initial delay and
     * between-event delay to {@code delay} milliseconds. If {@code delay}
     * is less than or equal to zero, the timer fires as soon as it
     * is started. If <code>listener</code> is not <code>null</code>,
     * it's registered as an action listener on the timer.
     *
     * @param delay milliseconds for the initial and between-event delay
     * @param listener  an initial listener; can be <code>null</code>
     *
     * @see #addActionListener
     * @see #setInitialDelay
     * @see #setRepeats
     */
    public Timer(int delay, ActionListener listener) {
        super();
        this.delay = delay;
        this.initialDelay = delay;

        doPostEvent = new DoPostEvent();

        if (listener != null) {
            addActionListener(listener);
        }
    }

    /*
     * The timer's AccessControlContext.
     */
     private transient volatile AccessControlContext acc =
            AccessController.getContext();

    /**
      * Returns the acc this timer was constructed with.
      */
     final AccessControlContext getAccessControlContext() {
       if (acc == null) {
           throw new SecurityException(
                   "Timer is missing AccessControlContext");
       }
       return acc;
     }

    /**
     * DoPostEvent is a runnable class that fires actionEvents to
     * the listeners on the EventDispatchThread, via invokeLater.
     * @see Timer#post
     */
    class DoPostEvent implements Runnable
    {
        public void run() {
            if (logTimers) {
                System.out.println("Timer ringing: " + Timer.this);
            }
            if(notify.get()) {
                fireActionPerformed(new ActionEvent(Timer.this, 0, getActionCommand(),
                                                    System.currentTimeMillis(),
                                                    0));
                if (coalesce) {
                    cancelEvent();
                }
            }
        }

        Timer getTimer() {
            return Timer.this;
        }
    }

    /**
     * Adds an action listener to the <code>Timer</code>.
     *
     * @param listener the listener to add
     *
     * @see #Timer
     */
    public void addActionListener(ActionListener listener) {
        listenerList.add(ActionListener.class, listener);
    }


    /**
     * Removes the specified action listener from the <code>Timer</code>.
     *
     * @param listener the listener to remove
     */
    public void removeActionListener(ActionListener listener) {
        listenerList.remove(ActionListener.class, listener);
    }


    /**
     * Returns an array of all the action listeners registered
     * on this timer.
     *
     * @return all of the timer's <code>ActionListener</code>s or an empty
     *         array if no action listeners are currently registered
     *
     * @see #addActionListener
     * @see #removeActionListener
     *
     * @since 1.4
     */
    public ActionListener[] getActionListeners() {
        return listenerList.getListeners(ActionListener.class);
    }


    /**
     * Notifies all listeners that have registered interest for
     * notification on this event type.
     *
     * @param e the action event to fire
     * @see EventListenerList
     */
    protected void fireActionPerformed(ActionEvent e) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();

        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i=listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==ActionListener.class) {
                ((ActionListener)listeners[i+1]).actionPerformed(e);
            }
        }
    }

    /**
     * Returns an array of all the objects currently registered as
     * <code><em>Foo</em>Listener</code>s
     * upon this <code>Timer</code>.
     * <code><em>Foo</em>Listener</code>s
     * are registered using the <code>add<em>Foo</em>Listener</code> method.
     * <p>
     * You can specify the <code>listenerType</code> argument
     * with a class literal, such as <code><em>Foo</em>Listener.class</code>.
     * For example, you can query a <code>Timer</code>
     * instance <code>t</code>
     * for its action listeners
     * with the following code:
     *
     * <pre>ActionListener[] als = (ActionListener[])(t.getListeners(ActionListener.class));</pre>
     *
     * If no such listeners exist,
     * this method returns an empty array.
     *
     * @param listenerType  the type of listeners requested;
     *          this parameter should specify an interface
     *          that descends from <code>java.util.EventListener</code>
     * @return an array of all objects registered as
     *          <code><em>Foo</em>Listener</code>s
     *          on this timer,
     *          or an empty array if no such
     *          listeners have been added
     * @exception ClassCastException if <code>listenerType</code> doesn't
     *          specify a class or interface that implements
     *          <code>java.util.EventListener</code>
     *
     * @see #getActionListeners
     * @see #addActionListener
     * @see #removeActionListener
     *
     * @since 1.3
     */
    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
        return listenerList.getListeners(listenerType);
    }

    /**
     * Returns the timer queue.
     */
    private TimerQueue timerQueue() {
        return TimerQueue.sharedInstance();
    }


    /**
     * Enables or disables the timer log. When enabled, a message
     * is posted to <code>System.out</code> whenever the timer goes off.
     *
     * @param flag  <code>true</code> to enable logging
     * @see #getLogTimers
     */
    public static void setLogTimers(boolean flag) {
        logTimers = flag;
    }


    /**
     * Returns <code>true</code> if logging is enabled.
     *
     * @return <code>true</code> if logging is enabled; otherwise, false
     * @see #setLogTimers
     */
    public static boolean getLogTimers() {
        return logTimers;
    }


    /**
     * Sets the <code>Timer</code>'s between-event delay, the number of milliseconds
     * between successive action events. This does not affect the initial delay
     * property, which can be set by the {@code setInitialDelay} method.
     *
     * @param delay the delay in milliseconds
     * @see #setInitialDelay
     */
    public void setDelay(int delay) {
        if (delay < 0) {
            throw new IllegalArgumentException("Invalid delay: " + delay);
        }
        else {
            this.delay = delay;
        }
    }


    /**
     * Returns the delay, in milliseconds,
     * between firings of action events.
     *
     * @see #setDelay
     * @see #getInitialDelay
     */
    public int getDelay() {
        return delay;
    }


    /**
     * Sets the <code>Timer</code>'s initial delay, the time
     * in milliseconds to wait after the timer is started
     * before firing the first event. Upon construction, this
     * is set to be the same as the between-event delay,
     * but then its value is independent and remains unaffected
     * by changes to the between-event delay.
     *
     * @param initialDelay the initial delay, in milliseconds
     * @see #setDelay
     */
    public void setInitialDelay(int initialDelay) {
        if (initialDelay < 0) {
            throw new IllegalArgumentException("Invalid initial delay: " +
                                               initialDelay);
        }
        else {
            this.initialDelay = initialDelay;
        }
    }


    /**
     * Returns the <code>Timer</code>'s initial delay.
     *
     * @see #setInitialDelay
     * @see #setDelay
     */
    public int getInitialDelay() {
        return initialDelay;
    }


    /**
     * If <code>flag</code> is <code>false</code>,
     * instructs the <code>Timer</code> to send only one
     * action event to its listeners.
     *
     * @param flag specify <code>false</code> to make the timer
     *             stop after sending its first action event
     */
    public void setRepeats(boolean flag) {
        repeats = flag;
    }


    /**
     * Returns <code>true</code> (the default)
     * if the <code>Timer</code> will send
     * an action event
     * to its listeners multiple times.
     *
     * @see #setRepeats
     */
    public boolean isRepeats() {
        return repeats;
    }


    /**
     * Sets whether the <code>Timer</code> coalesces multiple pending
     * <code>ActionEvent</code> firings.
     * A busy application may not be able
     * to keep up with a <code>Timer</code>'s event generation,
     * causing multiple
     * action events to be queued.  When processed,
     * the application sends these events one after the other, causing the
     * <code>Timer</code>'s listeners to receive a sequence of
     * events with no delay between them. Coalescing avoids this situation
     * by reducing multiple pending events to a single event.
     * <code>Timer</code>s
     * coalesce events by default.
     *
     * @param flag specify <code>false</code> to turn off coalescing
     */
    public void setCoalesce(boolean flag) {
        boolean old = coalesce;
        coalesce = flag;
        if (!old && coalesce) {
            // We must do this as otherwise if the Timer once notified
            // in !coalese mode notify will be stuck to true and never
            // become false.
            cancelEvent();
        }
    }


    /**
     * Returns <code>true</code> if the <code>Timer</code> coalesces
     * multiple pending action events.
     *
     * @see #setCoalesce
     */
    public boolean isCoalesce() {
        return coalesce;
    }


    /**
     * Sets the string that will be delivered as the action command
     * in <code>ActionEvent</code>s fired by this timer.
     * <code>null</code> is an acceptable value.
     *
     * @param command the action command
     * @since 1.6
     */
    public void setActionCommand(String command) {
        this.actionCommand = command;
    }


    /**
     * Returns the string that will be delivered as the action command
     * in <code>ActionEvent</code>s fired by this timer. May be
     * <code>null</code>, which is also the default.
     *
     * @return the action command used in firing events
     * @since 1.6
     */
    public String getActionCommand() {
        return actionCommand;
    }


    /**
     * Starts the <code>Timer</code>,
     * causing it to start sending action events
     * to its listeners.
     *
     * @see #stop
     */
     public void start() {
        timerQueue().addTimer(this, getInitialDelay());
    }


    /**
     * Returns <code>true</code> if the <code>Timer</code> is running.
     *
     * @see #start
     */
    public boolean isRunning() {
        return timerQueue().containsTimer(this);
    }


    /**
     * Stops the <code>Timer</code>,
     * causing it to stop sending action events
     * to its listeners.
     *
     * @see #start
     */
    public void stop() {
        getLock().lock();
        try {
            cancelEvent();
            timerQueue().removeTimer(this);
        } finally {
            getLock().unlock();
        }
    }


    /**
     * Restarts the <code>Timer</code>,
     * canceling any pending firings and causing
     * it to fire with its initial delay.
     */
    public void restart() {
        getLock().lock();
        try {
            stop();
            start();
        } finally {
            getLock().unlock();
        }
    }


    /**
     * Resets the internal state to indicate this Timer shouldn't notify
     * any of its listeners. This does not stop a repeatable Timer from
     * firing again, use <code>stop</code> for that.
     */
    void cancelEvent() {
        notify.set(false);
    }


    void post() {
         if (notify.compareAndSet(false, true) || !coalesce) {
             AccessController.doPrivileged(new PrivilegedAction<Void>() {
                 public Void run() {
                     SwingUtilities.invokeLater(doPostEvent);
                     return null;
                }
            }, getAccessControlContext());
        }
    }

    Lock getLock() {
        return lock;
    }

    private void readObject(ObjectInputStream in)
        throws ClassNotFoundException, IOException
    {
        this.acc = AccessController.getContext();
        in.defaultReadObject();
    }

    /*
     * We have to use readResolve because we can not initialize final
     * fields for deserialized object otherwise
     */
    private Object readResolve() {
        Timer timer = new Timer(getDelay(), null);
        timer.listenerList = listenerList;
        timer.initialDelay = initialDelay;
        timer.delay = delay;
        timer.repeats = repeats;
        timer.coalesce = coalesce;
        timer.actionCommand = actionCommand;
        return timer;
    }
}

实例

ps:AbstractEvent是Action子类,Action是ActionListener的子类

package com.company;

import javax.swing.*;
import java.applet.Applet;
import java.awt.*;
import java.awt.event.ActionEvent;

public class RotatingCursorCompact extends Applet {
    private Action updateCursorAction =new AbstractAction(){
        int ind=0;
        public void actionPerformed(ActionEvent e)
        {
            if(ind==0)
            {
                drawCursor(Cursor.NE_RESIZE_CURSOR);
            }
            else if(ind ==1)
            {
                drawCursor(Cursor.N_RESIZE_CURSOR);
            }
            else if(ind ==2)
            {
                drawCursor(Cursor.NW_RESIZE_CURSOR);
            }
            else if(ind ==3)
            {
                drawCursor(Cursor.W_RESIZE_CURSOR);
            }
            else if(ind ==4)
            {
                drawCursor(Cursor.SW_RESIZE_CURSOR);
            }
            else if(ind ==5)
            {
                drawCursor(Cursor.S_RESIZE_CURSOR);
            }
            else if(ind ==6)
            {
                drawCursor(Cursor.SE_RESIZE_CURSOR);
            }
            else if(ind ==7)
            {
                drawCursor(Cursor.E_RESIZE_CURSOR);
                ind=-1;
            }
            ind++;
        }
    };
    public void drawCursor(int cursorType)
    {
        setCursor(Cursor.getPredefinedCursor(cursorType));
    }
    public void init(){
        new Timer(300,updateCursorAction).start();
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值