Chapter 19
Exceptions and Events in Depth

by Clayton Walnum

W hen you write applets or applications using Java, sooner or later you're going to run into exceptions. An exception is a special type of error object that is created when something goes wrong in a program. After Java creates the exception object, it sends it to your program, an action called throwing an exception. It's up to your program to catch the exception. You do this by writing the exception-handling code. In this chapter, you get the inside information on these important error-handling objects.

Other types of objects that Java likes to pass around are events which represent actions that the user performs on your program, such as clicking a button or moving the mouse. As you will soon see, there are several ways your programs can handle events. This chapter gives you an in-depth look at the Event class and how it's used in your Java projects.

Javas Exceptions

In Chapter 18, "Streams and Files," you got a quick look at exceptions and how they are handled in a program. Specifically, you had to be prepared to handle an exception when you called some methods of the stream classes. This is because the method call may not be able to complete successfully (such as when trying to open a nonexistent file). In this case, Java throws an exception object called IOException. Listing 19.1 shows a code segment that handles this exception.

See "Exploring the main() Method," Chapter 18

Listing 19.1Handling an Exception

try
{
    System.in.read(buffer, 0, 255);
}
catch (IOException e)
{
    String err = e.toString();
    System.out.println(err);



}

As you can see from the listing, you place the code that may cause the exception in a try program block, whereas the exception-handling code goes into a catch program block. In this case, the first line of the try block attempts to read data from the standard input stream. If the read is unsuccessful, the read() method throws an IOException. When this happens, Java ignores the rest of the code in the try block and jumps to the catch block, where the program handles the exception. On the other hand, if the read goes okay, Java executes all the code in the try block and skips the catch block.


NOTE: The catch program block does more than direct program execution. It actually catches the exception object thrown by Java. In Listing 19.1 you can see the exception object being caught inside the parentheses following the catch keyword. This is very similar to a parameter being received by a method. In this case, the type of the "parameter" is IOException, and the name of the parameter is e. If you need to, you can access the exception object's methods through the e object, which is done in the example where the program calls e's toString() method in order to get a string representing the exception object.


Java defines many exception objects that may be thrown by the methods in Java's classes. How do you know which exceptions you have to handle? First, if you write an applet that calls a method that may throw an exception, Java insists that you handle the exception in one way or another. If you fail to do so, your applet does not compile. Instead, you receive an error message indicating where your program may generate the exception (see Figure 19.1).

FIG. 19.1
Java's compiler gives you an error message if you fail to handle an exception in your applet.

Although the compiler's error messages are a clue that something is amiss, the clever programmer will look up a method in Java's documentation before using the method. Then the programmer will know in advance whether that method requires exception-handling code. If you're interested in seeing the exceptions that are defined by a package, find the package's section in Java's online documentation (see Figure 19.2) where the classes and exceptions are listed.

FIG. 19.2
Java's online docu-mentation lists the exception objects that may be thrown by methods in a class.


TIP: The online documentation on Sun's Web site is constantly being updated. To stay up-to-date, set a bookmark in your browser for http://www.javasoft.com/products/JDK/ and visit the site often.


The online documentation also lists all the methods that comprise a particular package. By looking up the method in the documentation (see Figure 19.3), you can see what types of arguments the method expects, the type of value the method returns, and whether the method may throw an exception. If the method shows that it can throw an exception, your code must handle the right type of exception, or the program will not compile.

FIG. 19.3
The online documentation for a method shows the exception the method may throw.

Throwing an Exception

One handy thing about exceptions is that you don't have to handle them in the same method in which the exception is generated. For example, in Listing 19.1, the applet tries to read data from the standard input device. If the read fails, the method throws an exception that the program handles in its catch block.

But what if, for some reason, you don't want to handle the exception in the same method in which you call the read() method? You can simply pass the buck, so to speak, by throwing the exception on up the method hierarchy. Listing 19.2 shows one way you might do this with the IOException exception.

Listing 19.2LST19_02.TXT--Throwing an Exception

protected void MyMethod() {
    try
    {
        DoRead();
    }
    catch (IOException e)
    {
        String err = e.toString();
        System.out.println(err);
    }
}

protected void DoRead() throws IOException
{
    System.in.read(buffer, 0, 255);
}

In this listing, the call to the read() method has been moved to a method called DoRead(). However, DoRead() does not directly handle the IOException exception. Instead, it passes the exception back to the calling method. Java knows that DoRead() wants to pass the exception, because DoRead() adds the phrase throws IOException to its signature. Throwing the exception, however, doesn't relieve you from handling it eventually. Notice that in Listing 19.2, the exception still gets handled in the calling method.

In short, you can handle an exception in two ways:

A Combined Approach

There may be times in your programs when you want to both handle an exception in your code and pass it on to the calling function. Java allows you to construct your code this way, so that different parts of a program can handle an exception as is appropriate for that part of the program. To use this combined approach to exception handling, include both try-catch program blocks and a throws clause in the method. Listing 19.3 shows an example of this handy technique.

Listing 19.3LST19_03TXT--Code that Both Handles and Passes on an Exception

protected void MyMethod() throws IOException {
    try
    {
        DoRead();
    }
    catch (IOException e)
    {
        String err = e.toString();
        System.out.println(err);
    }
}

As you've seen in the last few examples, exception objects can do a lot of traveling. They jump from method to method, up the hierarchy of method calls until someone finally deals with them. If the exception makes its way up to the Java system, the system handles it in some default manner, usually by generating an error message. However, when running applets in a browser, the user may not get a chance to see the error messages. Worse, some Java-compatible browsers will likely handle exceptions differently from others. One browser may just ignore the exception and keep on chugging, whereas another may be stricken to its digital knees. The best approach is to handle any exceptions that may occur in your program. That way, you can be pretty sure that the browser will remain unaffected by the error.

Types of Exceptions

Java defines many different exception objects. Some of these you must always handle in your code if you call a function that may throw the exception. Others are generated by the system when something like memory allocation fails, an expression tries to divide by zero, a null value is used inappropriately, and so on. You can choose to watch for this second kind of exception or let Java deal with them. All of these types of exceptions are derived from the RuntimeException class.

Just as with programming before exceptions existed, you should always be on the lookout for places in your program where an exception could be generated. These places are usually associated with user input, which can be infamously unpredictable. However, programmers, too, have been known to make mistakes in their programs that lead to exception throwing. Some common exceptions you may want to watch out for at appropriate places in your applet are listed in Table 19.1.
Table 19.1 Common Java Exceptions
Exception Caused By
ArithmeticException Math errors, such as division by zero.
ArrayIndexOutOfBounds Bad array indexes. Exception
ArrayStoreException A program trying to store the wrong type of data in an array.
FileNotFoundException An attempt to access a nonexistent file.
IOException General I/O failures, such as inability to read from a file.
NullPointerException Referencing a null object.
NumberFormatException A failing conversion between strings and numbers.
OutOfMemoryException Too little memory to allocate a new object.
SecurityException An applet trying to perform an action not allowed by the browser's security setting.
StackOverflowException The system running out of stack space.
StringIndexOutOfBoundsException A program attempting to access a nonexistent character position in a string.


TIP: You can catch all types of exceptions by setting up your catch block for exceptions of type Exception, like this:

catch (Exception e)

Call the exception's getMessage() method (inherited from the Throwable superclass) to get information about the specific exception that you've intercepted.


All of Java's exceptions are organized into a class hierarchy. At the root of this hierarchy is the Throwable class, to which all exception and error objects can trace their ancestry. (You learn about error objects later in this chapter, in the section "Java's Error Classes.") The Throwable class defines three useful methods that you can call to get information about an exception:

Listing 19.4 shows a catch clause that calls these various methods, whereas Figure 19.4 shows the output from the catch clause. (Notice that in the case of NumberFormatException, the getMessage() method returns an empty string.)

Listing 19.4LST19_04.TXT--Calling a Throwable Objects Methods

catch (NumberFormatException e)
{
    System.out.println();
    System.out.println("Here's getMessage()'s string:");
    System.out.println("-----------------------------");
    String str = e.getMessage();
    System.out.println(str);
    System.out.println();
    System.out.println("Here's toString()'s string:");
    System.out.println("---------------------------");
    str = e.toString();
    System.out.println(str);
    System.out.println();
    System.out.println("Here's the stack trace:");
    System.out.println("-----------------------");
    e.printStackTrace();
}

FIG. 19.4
Here's the output generated by the catch block in Listing 19.4.

Next in the hierarchy after Throwable is the Exception class. That is, Exception is derived directly from Throwable (as is Error). However, the Exception class provides no useful methods beyond its constructors; it is the base class for all exception classes in the Java system.

After Exception, the hierarchy divides into three main categories:

Java's many exception classes are listed as they appear in the class hierarchy. The package in which a class is defined is shown in parentheses after the class name.


Throwable (java.lang)
    Exception (java.lang)
        AWTException (java.awt)
        NoSuchMethodException (java.lang)
        InterruptedException (java.lang)
        InstantiationException (java.lang)
        ClassNotFoundException (java.lang)
        CloneNotSupportedException (java.lang)
        IllegalAccessException (java.lang)
        IOException (java.io)
            EOFException (java.io)
            FileNotFoundException (java.io)
            InterruptedIOException (java.io)
            UTFDataFormatException (java.io)
            MalformedURLException (java.net)
            ProtocolException (java.net)
            SocketException (java.net)
            UnknownHostException (java.net)
            UnknownServiceException (java.net)
        RuntimeException (java.lang)
            ArithmeticException (java.lang)
            ArrayStoreException (java.lang)
            ClassCastException (java.lang)
            IllegalArgumentException (java.lang)
                IllegalThreadStateException (java.lang)
                NumberFormatException (java.lang)
            IllegalMonitorStateException (java.lang)
            IndexOutOfBoundsException (java.lang)
                ArrayIndexOutOfBoundsException (java.lang)
                StringIndexOutOfBoundsException (java.lang)
            NegativeArraySizeException (java.lang)
            NullPointerException (java.lang)
            SecurityException (java.lang)
            EmptyStackException (java.util)
            NoSuchElementException(java.util)


NOTE: The list of exceptions shown here was created from the original Java classes. The newest version of Java adds many new classes, and so adds many new exceptions, as well.


Determining the Exceptions to Handle

Experienced programmers usually know when their code may generate an exception of some sort. However, when you first start writing applets with exception-handling code, you may not be sure what type of exceptions to watch out for. One way to discover this information is to see what exceptions get generated as you test your applet.

Listing 19.5, for example, is an applet called ExceptionApplet that divides two integer numbers obtained from the user and displays the integer result (dropping any remainder). Because the applet must deal with user input, the probability of disaster is high. ExceptionApplet, however, contains no exception-handling code.

Listing 19.5ExceptionApplet.java--An Applet with No Exception Handling

import java.awt.*;
import java.applet.*;
public class ExceptionApplet extends Applet
{
    TextField textField1, textField2;
    String answerStr;
    public void init()
    {
        textField1 = new TextField(15);
        add(textField1);
        textField2 = new TextField(15);
        add(textField2);
        answerStr = "Undefined";
    }
    public void paint(Graphics g)
    {
        Font font = new Font("TimesRoman", Font.PLAIN, 24);
        g.setFont(font);
        g.drawString("The answer is:", 50, 100);
        g.drawString(answerStr, 70, 130);
    }
    public boolean action(Event evt, Object arg)
    {
        String str1 = textField1.getText();
        String str2 = textField2.getText();
        int int1 = Integer.parseInt(str1);
        int int2 = Integer.parseInt(str2);
        int answer = int1 / int2;
        answerStr = String.valueOf(answer);
        repaint();
        return true;
    }
}

You'll use this applet as the starting point for a more robust applet. When you run the applet using Applet Viewer, you see the window shown in Figure 19.5. Enter a number into each of the two text boxes and then press Enter. The program then divides the first number by the second number and displays the result (see Figure 19.6).

FIG. 19.5
ExceptionApplet
is running under Applet Viewer.

FIG. 19.6
ExceptionApplet divides the first number by the second.

As long as the user enters valid numbers into the text boxes, the program runs perfectly.

What happens, though, if the user presses Enter when either or both of the text boxes are empty? Java immediately throws a NumberFormatException when the action() method attempts to convert the contents of the text boxes to integer values. You can see this happening by watching the command-line window from which you ran Applet Viewer, as shown in Figure 19.7. As you can see in the figure, Java has displayed quite a few lines that trace the exception. The first line (the one that starts with the word Exception) tells you the type of exception you've encountered.

FIG. 19.7
Here, Java reports a NumberFormat- Exception exception.


As you now know, you don't have to catch every exception that Java can produce. When you fail to provide code for an exception that doesn't require catching, Java catches the exception internally. When this happens to an applet running under Applet Viewer, you see an exception error appear in the command-line window. However, if an applet generates an exception while running in a Web browser, the user is probably never aware of it, because the applet doesn't usually crash or display errors; it just fails to perform the command that generated the exception.


Catching a Runtime Exception

You now know that users can cause a NumberFormatException if they leave one or more text boxes blank or enter an invalid numerical value, like the string one. In order to ensure that your applet is not caught by surprise, you now need to write the code that will handle this exception. Follow these steps to add this new code:

  1. Load ExceptionApplet into your text editor.

  2. Replace the action() method with the new version shown in Listing 19.6.

  3. In the class declaration line, change the name of the class to ExceptionApplet2.

  4. Save the new applet under the name ExceptionApplet2.java.

  5. Load the EXCEPTIONAPPLET.HTML file.

  6. Change all occurrences of ExceptionApplet to ExceptionApplet2.

  7. Save the file as EXCEPTIONAPPLET2.HTML.

Listing 19.6LST19_06.TXT--Handling the NumberFormatException Exception

public boolean action(Event evt, Object arg)
{
    String str1 = textField1.getText();
    String str2 = textField2.getText();
    try
    {
        int int1 = Integer.parseInt(str1);
        int int2 = Integer.parseInt(str2);
        int answer = int1 / int2;
        answerStr = String.valueOf(answer);
    }
    catch (NumberFormatException e)
    {
        answerStr = "Bad number!";
    }
    repaint();
    return true;
}

In Listing 19.6, the action() method now uses try and catch program blocks to handle the NumberFormatException gracefully. Figure 19.8 shows what happens now when the user leaves the text boxes blank. When the program gets to the first call to String.valueOf(), Java generates the NumberFormatException exception, which causes program execution to jump to the catch block. In the catch block, the program sets the display string to Bad number!. The call to repaint() ensures that this message to the user is displayed on-screen.

FIG. 19.8
ExceptionApplet2
handles the NumberFormat-Exception exception gracefully.

Handling Multiple Exceptions

So, here you are, having a good time entering numbers into ExceptionApplet2's text boxes and getting the results. Without thinking, you enter a zero into the second box, Java tries to divide the first number by the zero, and pow!--you've got yourself an ArithmeticException exception. What to do? You're already using your catch block to grab NumberFormatException; now, you've got yet another exception to deal with.

The good news is that you're not limited to only a single catch block. You can, in fact, create catch blocks for any exceptions you think the program may generate. To see how this works with your new applet, follow these steps:

  1. Load ExceptionApplet2 into your text editor.

  2. Replace the action() method with the new version shown in Listing 19.7.

  3. In the class declaration line, change the name of the class to ExceptionApplet3.

  4. Save the new applet under the name ExceptionApplet3.java.

  5. Load the EXCEPTIONAPPLET.HTML file.

  6. Change all occurrences of ExceptionApplet to ExceptionApplet3.

  7. Save the file as EXCEPTIONAPPLET3.HTML.

Listing 19.7LST19_07.TXT--Handling Multiple Exceptions

public boolean action(Event evt, Object arg)
{
    String str1 = textField1.getText();
    String str2 = textField2.getText();
    try
    {
        int int1 = Integer.parseInt(str1);
        int int2 = Integer.parseInt(str2);
        int answer = int1 / int2;
        answerStr = String.valueOf(answer);
    }
    catch (NumberFormatException e)
    {
        answerStr = "Bad number!";
    }
    catch (ArithmeticException e)
    {
        answerStr = "Division by 0!";
    }
    repaint();
    return true;
}

If you examine Listing 19.7, you see that the action() method now defines two catch program blocks, one each for the NumberFormatException and ArithmeticException exceptions. In this way, the program can watch for both potential problems from within a single try block. Figure 19.9 shows what ExceptionApplet3 looks like when the user attempts a division by zero. If you discovered another exception that your program may cause, you can add yet another catch block.

FIG. 19.9
ExceptionApplet3
catches division-by-zero errors.


NOTE: Although handling exceptions is a powerful tool for creating crash-proof programs, you should use them only in situations where you have little control over the cause of the exception, such as when dealing with user input. If your applet causes an exception because of a program bug, you should track down and fix the problem rather than try to catch the exception.


TIP: There may be times when you want to be sure that a specific block of code gets executed whether or not an exception is generated. You can do this by adding a finally program block after the last catch. The code in the finally block gets executed after the try block or catch block finishes its thing. Listing 19.8 shows an example.


Listing 19.8LST19_08.TXT--Using the finally Program Block

try
{
    // The code that may generate an exception goes here.
}
catch (Exception e)
{
    // The code that handles the exception goes here.
}
finally
{
    // The code here is executed after the try or
    // catch blocks finish executing.
}

Creating Your Own Exception Classes

Although Java provides exception classes for just about every general error you can imagine, the designers of the language couldn't possibly know what type of code you're going to write and what kinds of errors that code may experience. For example, you may write a method that sums two numbers within a specific range. If the user enters a value outside of the selected range, your program could throw a custom exception called something like NumberRangeException.

To create and throw your own custom exceptions, you must first define a class for the exception. Usually, you derive this class from Java's Exception class. Listing 19.9 shows how you might define the aforementioned NumberRangeException class.

Listing 19.9NumberRangeException.java--The NumberRangeException Class

public class NumberRangeException extends Exception
{
    public NumberRangeException(String msg)
    {
	  super(msg);
    }
}

As you can see, defining a new exception requires little work. In fact, you can get by with just creating a constructor for the class. Notice that the NumberRangeException class's constructor receives a String parameter. This string is the detail message that the class returns if you call its getMessage() method (which the class inherits from Throwable through Exception). Inside the constructor, this string is passed on up to NumberRangeException's superclass (Exception), which itself passes the string on up to the Throwable class where it is stored as a data member of the class. Now, inside your program, wherever you determine that your custom-exception condition has occurred, you can create and throw an object of your exception class.

Listing 19.10 is an applet that puts the new NumberRangeException to the test. When you run the applet, type a number into each text box. If you follow the directions, typing two numbers within the range 10-20, the applet sums the numbers and displays the results. Otherwise, the applet generates a NumberRangeException exception and displays an error message, as shown in Figure 19.10.


TIP: When you compile the ExceptionApplet4 applet, make sure the NumberRangeException.java file is in the same directory as the applet's source code. Otherwise, the Java compiler may not be able to find it. You may also need to add the applet's path to the CLASSPATH environment variable.


Listing 19.10ExceptionApplet4.java--An Applet that Incorporates a Custom Exception Class

import java.awt.*;
import java.applet.*;
public class ExceptionApplet4 extends Applet
{
    TextField textField1, textField2;
    String answerStr;
    public void init()
    {
        textField1 = new TextField(15);
        add(textField1);
        textField2 = new TextField(15);
        add(textField2);
        answerStr = "Undefined";
        resize(500, 200);
    }
    public void paint(Graphics g)
    {
        Font font = new Font("TimesRoman", Font.PLAIN, 24);
        g.setFont(font);
        g.drawString("Enter numbers between", 40, 70);
        g.drawString("10 and 20.", 70, 90); 
        g.drawString("The answer is:", 40, 130);
        g.drawString(answerStr, 70, 150);
    }
    public boolean action(Event evt, Object arg)
    {
        try
        {
            int answer = CalcAnswer();
            answerStr = String.valueOf(answer);
        }
        catch (NumberRangeException e)
        {
            answerStr = e.getMessage();
        }
        repaint();
        return true;
    }
    public int CalcAnswer() throws NumberRangeException
    {
        int int1, int2;
        int answer = -1;
        String str1 = textField1.getText();
        String str2 = textField2.getText();
        try
        {
            int1 = Integer.parseInt(str1);
            int2 = Integer.parseInt(str2);
            if ((int1 < 10) || (int1 > 20) ||
                (int2 < 10) || (int2 > 20))
            {
                NumberRangeException e = new NumberRangeException
                   ("Numbers not within the specified range.");
                throw e;
            }
            answer = int1 + int2;
        }
        catch (NumberFormatException e)
        {
           answerStr = e.toString();
        }
        return answer;
    }
}

FIG. 19.10
This applet catches NumberRange- Exception exceptions.

In the ExceptionApplet4 applet's action() method, the program calls the local CalcAnswer() method. The action() method must enclose this method call in try and catch program blocks because CalcAnswer() throws a NumberRangeException exception (the exception class you just created). In CalcAnswer(), the program extracts the strings the user typed into the text boxes and converts the returned strings to integers. Because the parseInt() method calls can throw NumberFormatException exceptions, CalcAnswer() encloses the calls to parseInt() within a try program block. In the try block, the program not only converts the strings to integers, but also checks whether the integers fall within the proper range of values. If they don't, the program creates and throws an object of the NumberRangeException class.

Javas Error Classes

So far, you've had a look at the exception classes you can handle in your own programs. Java also defines a set of Error classes that are really little more than special types of exceptions. Like the class Exception, the class Error is derived from Throwable. However, the more specific error classes that are derived from Error represent serious errors, such as internal errors or problems with a class, that your program shouldn't fool with. The Java system handles these errors for you.

The following is a list of the error classes organized into their inheritance hierarchy. The package in which a class is defined is shown in parentheses after the class's name. (All but one are defined in java.lang.)

Throwable (java.lang)
    Error (java.lang)
        AWTError (java.awt)
        ThreadDeath (java.lang)
        LinkageError (java.lang)
            ClassCircularityError (java.lang)
            ClassFormatError (java.lang)
            NoClassDefFoundError (java.lang)
            UnsatisfiedLinkError (java.lang)
            VerifyError (java.lang)
            IncompatibleClassChangeError (java.lang)
                AbstractMethodError (java.lang)
                IllegalAccessError (java.lang)
                InstantiationError (java.lang)
                NoSuchFieldError (java.lang)
                NoSuchMethodError (java.lang)
        VirtualMachineError (java.lang)
            InternalError (java.lang)
            OutOfMemoryError (java.lang)
            StackOverflowError (java.lang)
            UnknownError (java.lang)

Javas Events

As you know, events represent all the activity that goes on between a program, the system, and the program's user. When the user does something with the program--such as click the mouse in the program's window--the system creates an event representing the action and ships it off to your program's event-handling code. This code determines how to handle the event so that the user gets the appropriate response.

For example, when the user clicks a button, he expects the command associated with that button to be executed. In Chapter 15, "Advanced Applet Code," you got a quick look at how you can use events in your applets. Now, it's time to examine Java's events in depth by exploring the classes that deal with events, as well as how to create and handle events.


NOTE: The latest version of Java 1.1 still allows Java programs to handle events as described in the following sections. However, the new version enhances the event-handling process, offering a new way to deal with events in programs. For the details, please see the section entitled "New Event-Handling Techniques for Java 1.1" later in this chapter. The following sections describe the event-handling architecture for the Java 1.0 event model, with which most developers are already familiar.


See "Java's Events," Chapter 19

The Event Class

Under Java, events are actually objects of a class. This class, called appropriately enough Event, defines all the events to which a program can respond, as well as defines default methods for extracting information about the event. When all is said and done, Event is a fairly complex class, as you soon see.

The first thing the Event class does is define constants for the many keys that can either constitute an event (such as a key-down event) or be used to modify an event (such as holding down Shift when mouse-clicking). Table 19.2 lists these constants and their descriptions.
Table 19.2 Keyboard Constants of the Event Class
Constant Key
ALT_MASK Alt (Alternate) key
CTRL_MASK Ctrl
DOWN Down arrow
END End
F1 F1
F10 F10
F11 F11
F12 F12
F2 F2
F3 F3
F4 F4
F5 F5
F6 F6
F7 F7
F8 F8
F9 F9
HOME Home
LEFT Left arrow
META_MASK Meta
PGDN Page Down
PGUP Page Up
RIGHT Right arrow
SHIFT_MASK Shift
UP Up arrow


Next, the Event class defines constants for all the events that can be handled in a Java program. These events include everything from basic mouse and keyboard events to the events generated by moving, minimizing, or closing windows. Table 19.3 lists these event constants, which are used as IDs for Event objects.
Table 19.3 Event Constants of the Event Class
Constant Description
ACTION_EVENT Used in support of the action() method
GOT_FOCUS Generated when a window (or component) gets the input focus
KEY_ACTION Similar to KEY_PRESS
KEY_ACTION_RELEASE Similar to KEY_RELEASE
KEY_EVENT A general keyboard event
KEY_PRESS Generated when a key is pressed
KEY_RELEASE Generated when a key is released
LIST_DESELECT Generated by deselecting an item in a list
LIST_EVENT A general list box event
LIST_SELECT Generated by selecting an item in a list
LOAD_FILE Generated when a file is loaded
LOST_FOCUS Generated when a window (or component) loses focus
MISC_EVENT A miscellaneous event
MOUSE_DOWN Generated when the mouse button is pressed
MOUSE_DRAG Generated when the mouse pointer is dragged
MOUSE_ENTER Generated when the mouse pointer enters a window
MOUSE_EVENT A general mouse event
MOUSE_EXIT Generated when the mouse pointer exits a window
MOUSE_MOVE Generated when the mouse pointer is moved
MOUSE_UP Generated when the mouse button is released
SAVE_FILE Generated when a file is saved
SCROLL_ABSOLUTE Generated by moving the scroll box
SCROLL_EVENT A general scrolling event
SCROLL_LINE_DOWN Generated by clicking the scrollbar's down arrow
SCROLL_LINE_UP Generated by clicking the scrollbar's up arrow
SCROLL_PAGE_DOWN Generated by clicking below the scroll box
SCROLL_PAGE_UP Generated by clicking above the scroll box
WINDOW_DEICONIFY Generated when a window is restored
WINDOW_DESTROY Generated when a window is destroyed
WINDOW_EVENT A general window event
WINDOW_EXPOSE Generated when a window is exposed
WINDOW_ICONIFY Generated when a window is minimized
WINDOW_MOVED Generated when a window is moved
Like most classes, the Event class declares a number of data members that it uses to store information about an event object. You might examine one or more of these data members when responding to an event. For example, when responding to most mouse events, you usually want to know the X and Y coordinates of the mouse when the event occurred. Table 19.4 lists the data members and their descriptions.
Table 19.4 Data Members of the Event Class
Data Member Description
arg Additional information about the event
clickCount Number of mouse clicks associated with the event
evt Next event in the list
id Event's ID (refer to Table 19.3)
key Keyboard event's key
keyChar Character key that was pressed
modifiers Event's modifier keys (refer to Table 19.2)
target Component that generated the event
when Event's timestamp
x Event's X coordinate
y Event's Y coordinate
Last, but surely not least, the Event class defines a number of methods that you can use to retrieve information about the event. Table 19.5 lists these methods and their descriptions.
Table 19.5 Methods of the Event Class
Method Description
controlDown() Gets the status of the Ctrl key
metaDown() Gets the status of a meta key
paramString() Gets the event's parameter string
shiftDown() Gets the status of the Shift key
toString() Gets a string representing the object's status
translate() Translates the event so that its x and y position are increased or decreased

An Events Genesis

You may wonder exactly where the events that arrive at your program come from. An operating system such as Microsoft Windows or Macintosh's System 7 tracks all the events occurring in the system. The system routes these events to the appropriate target objects. For example, if the user clicks your applet's window, the system constructs a mouse-down event and sends it off to the window for processing. The window can then choose to do something with the event or just pass it back to the system for default processing.

In the case of Java, the Java 1.0 event model intercepts events that are meant for Java components, translating and routing them as appropriate. Because all of this event-handling stuff is dependent upon the current windowing system being used, Java deals with events in the classes defined in the java.awt package. Specifically, the Component class receives and processes events for any class derived from Component. Because virtually every visible object (buttons, panels, text boxes, canvases, and more) in a Java 1.0 application or applet can trace its ancestry back to Component, Component is the event-handling granddaddy of them all. As such, the Component class defines many event-related methods. Table 19.6 lists these methods and their description.
Table 19.6 Event-Handling Methods of the Component Class
Method Description
action() Responds to components that have action events
deliverEvent() Sends an event to the component
handleEvent() Routes events to the appropriate handler
keyDown() Responds to key-down events
keyUp() Responds to key-up events
mouseDown() Responds to mouse-down events
mouseDrag() Responds to mouse-drag events
mouseEnter() Responds to mouse-enter events
mouseExit() Responds to mouse-exit events
mouseMove() Responds to mouse-move events
mouseUp() Responds to mouse-up events
postEvent() Similar to deliverEvent()


In the Component class, event-handling methods like action(), mouseDown(), and keyDown() don't actually do anything except return false, which indicates to Java that the event hasn't yet been handled. These methods are meant to be overridden in your programs so that the program can respond to the event as is appropriate. For example, if you haven't overridden mouseDown() in an applet, the default version of mouseDown() returns false, which tells Java that the message needs to be handled further on down the line. In the case of a mouse-down event, Java probably returns the unhandled event to the system for default handling (meaning that the event is effectively ignored).

The applet in Listing 19.11 responds to mouse clicks by printing the word Click! wherever the user clicks in the applet. It does this by overriding the mouseDown() method and storing the coordinates of the mouse click in the applet's coordX and coordY data fields. The paint() method then uses these coordinates to display the word. Figure 19.11 shows MouseApplet running under Applet Viewer.

See "Java's Events," Chapter 19

Listing 19.11MouseApplet.java--Using Mouse Clicks in an Applet

import java.awt.*;
import java.applet.*;
public class MouseApplet extends Applet
{
    int coordX, coordY;
    public void init()
    {
        coordX = -1;
        coordY = -1;
        Font font =
            new Font("TimesRoman", Font.BOLD, 24);
        setFont(font);
        resize(400, 300);
    }
    public void paint(Graphics g)
    {
        if (coordX != -1)
            g.drawString("Click!", coordX, coordY);
    }
    public boolean mouseDown(Event evt, int x, int y)
    {
        coordX = x;
        coordY = y;
        repaint();
        return true;
    }
}

FIG. 19.11
The MouseApplet applet responds to mouse clicks.


When you run MouseApplet, you discover that the applet window gets erased each time the paint() method is called. That's why only one Click! ever appears in the window.


The Keyboard

The keyboard has been around even longer than the mouse and has been the primary interface between humans and their computers for decades. Given the keyboard's importance, obviously there may be times when you want to handle the keyboard events at a lower level than you can with something like a TextField component. Java responds to two basic key events, which are represented by the KEY_PRESS and KEY_RELEASE constants. As you soon see, Java defines methods that make it just as easy to respond to the keyboard as it is to respond to the mouse. You got an introduction to keyboard events in Chapter 15, "Advanced Applet Code." In this section, you learn even more about how to deal with the keyboard in your Java programs.

See "Java's Events," Chapter 19

Whenever the user presses a key when an applet is active, Java sends the applet a KEY_PRESS event. In your Java program, you can respond to this event by overriding the keyDown() method, whose signature looks like this:

public boolean keyDown(Event evt, int key)

As you can see, this method receives two arguments, which are an Event object and an integer representing the key that was pressed. This integer is actually the ASCII representation of the character represented by the key. In order to use this value in your programs, however, you must first cast it to a char value, like this:

char c = (char)key;

Some of the keys on your keyboard issue commands rather than generate characters. These keys include all the F keys, as well as keys like Shift, Ctrl, Page Up, Page Down, and so on. In order to make these types of keys easier to handle in your applets, Java's Event class defines a set of constants that represent these keys' values (refer to Table 19.2).

The Event class also defines a number of constants for modifier keys that the user might press along with the basic key. These constants, which are also listed in Table 19.2, include ALT_MASK, SHIFT_MASK, and CTRL_MASK, which represent the Alt (or Alternate), Shift, and Ctrl (or Control) keys on your keyboard. The SHIFT_MASK and CTRL_MASK constants are used in the Event class's methods shiftDown() and controlDown(), each of which returns a boolean value indicating whether the modifier key is pressed. (There currently is no altDown() method.) You can also examine the Event object's modifiers field to determine whether a particular modifier key was pressed. For example, if you wanted to check for the Alt key, you might use a line of Java code like this:

boolean altPressed = (evt.modifiers & Event.ALT_MASK) != 0;

By ANDing the mask with the value in the modifiers field, you end up with a non-zero value if the Alt key was pressed and a 0 if it wasn't. You convert this result to a boolean value by comparing the result with 0.

Handling Events Directly

All of the events received by your applet using the Java 1.0 event model are processed by the handleEvent() method, which is inherited from the Component class. When this method is not overridden in your program, the default implementation is responsible for calling the many methods that respond to events. Listing 19.12 shows how the handleEvent() method is implemented in the Component class. By examining this listing, you can easily see why you only have to override methods like mouseDown() to respond to events. In the next section, you see how to customize handleEvent() in your own programs.

Listing 19.12LST19_12.TXT--The Default Implementation of handleEvent()

public boolean handleEvent(Event evt) {
switch (evt.id) {
  case Event.MOUSE_ENTER:
    return mouseEnter(evt, evt.x, evt.y);
  case Event.MOUSE_EXIT:
    return mouseExit(evt, evt.x, evt.y);
  case Event.MOUSE_MOVE:
    return mouseMove(evt, evt.x, evt.y);
  case Event.MOUSE_DOWN:
    return mouseDown(evt, evt.x, evt.y);
  case Event.MOUSE_DRAG:
    return mouseDrag(evt, evt.x, evt.y);
  case Event.MOUSE_UP:
    return mouseUp(evt, evt.x, evt.y);
  case Event.KEY_PRESS:
  case Event.KEY_ACTION:
    return keyDown(evt, evt.key);
  case Event.KEY_RELEASE:
  case Event.KEY_ACTION_RELEASE:
    return keyUp(evt, evt.key);
    
  case Event.ACTION_EVENT:
    return action(evt, evt.arg);
  case Event.GOT_FOCUS:
    return gotFocus(evt, evt.arg);
  case Event.LOST_FOCUS:
    return lostFocus(evt, evt.arg);
}
return false;
}

Overriding the handleEvent() Method

Although the default implementation of handleEvent() calls special methods that you can override in your program for each event, you might want to group all your event handling into one method to conserve on overhead, change the way an applet responds to a particular event, or even create your own events. To accomplish any of these tasks (or any others you might come up with), you can forget the individual event-handling methods and override handleEvent() instead.

In your version of handleEvent(), you must examine the Event object's id field in order to determine which event is being processed. You can just ignore events in which you're not interested. However, be sure to return false whenever you ignore a message, so that Java knows that it should pass the event on up the object hierarchy. Listing 19.13 is an applet that overrides the handleEvent() method in order to respond to events.

Listing 19.13DrawApplet2.java--Using the handleEvent() Method

import java.awt.*;
import java.applet.*;
public class DrawApplet2 extends Applet
{
    Point startPoint;
    Point points[];
    int numPoints;
    boolean drawing;
    public void init()
    {
        startPoint = new Point(0, 0);
        points = new Point[1000];
        numPoints = 0;
        drawing = false;
        resize(400, 300);
    }
    public void paint(Graphics g)
    {
        int oldX = startPoint.x;
        int oldY = startPoint.y;
        for (int x=0; x<numPoints; ++x)
        {
            g.drawLine(oldX, oldY, points[x].x, points[x].y);
            oldX = points[x].x;
            oldY = points[x].y;
        }
    }
    public boolean handleEvent(Event evt)
    {
        switch(evt.id)
        {
            case Event.MOUSE_DOWN:
                drawing = true;
                startPoint.x = evt.x;
                startPoint.y = evt.y;
                return true;
            case Event.MOUSE_MOVE:
                if ((drawing) && (numPoints < 1000))
                {
                    points[numPoints] = new Point(evt.x, evt.y);
                    ++numPoints;
                    repaint();
                }
                return true;

            default:

                return false;
        }
    }
}


NOTE: In Listing 19.13, the program overloads handleEvent() in order to be able to handle events at a lower level. However, one side effect of this technique is that events other than those explicitly handled in the new version of handleEvent() are ignored. If you still want to respond normally to all other events, you have to be sure to include them in your version of handleEvent(), or, even easier, just call the original version of handleEvent() from your new version, using the line super.handleEvent(evt) in place of the return false.


Sending Your Own Events

There may be times when the events created and routed by Java don't completely fit your program's needs. In those cases, you can create and send your own events. For example, you may want the user to be able to select a command both by clicking a button or pressing a key. One way you could handle this need is to have almost exactly the same event-handling code in your action() and keyDown() methods. The code in action() would handle the button click, and the code in keyDown() would handle the key press, as shown in Listing 19.14.

Listing 19.14LST19_14.TXT--Handling Events with Duplicate Code

public boolean action(Event evt, Object arg)
{
    if (arg == "Test Button")
    {
        if (color == Color.black)
            color = Color.red;
        else
            color = Color.black;
        repaint();
        return true;
    }
    return false;
}
public boolean keyDown(Event evt, int key)
{
    if ((key == LOWERCASE_T) || (key == UPPERCASE_T))
    {
        if (color == Color.black)
            color = Color.red;
        else
            color = Color.black;
        repaint();
        return true;
    }
    return false;
}

A more elegant solution to the problem presented in Listing 19.14 is to create your own event in response to a key press and then deliver that event to the button component. You can create your own event by calling the Event class's constructor, like this:

Event event = new Event(button1, Event.ACTION_EVENT, "Test Button");

The three required arguments are the event's target component, the event ID, and the additional information that's appropriate for the type of event. For a button action event, the third argument should be the button's label.

Once you have the event constructed, sending it is as easy as calling the deliverEvent() method, like this:

deliverEvent(event);

This method's single argument is the event object you want to deliver.

Listing 19.15 is an applet that creates and sends its own events in order to link key presses to button clicks. In the applet, when you click the button, the text color changes. The color also changes when you press the keyboard's T key. This is because the keyDown() method watches for T key presses (both upper- and lowercase). When keyDown() gets a T key press, it creates an ACTION_EVENT event and delivers it. This causes Java to call the action() method with the event, same as if the user had clicked the button. Figure 19.12 shows EventApplet running under Applet Viewer.

Listing 19.15EventApplet.java--Creating and Delivering Events

import java.awt.*;
import java.applet.*;
public class EventApplet extends Applet
{
    Button button1;
    String str;
    Color color;
    final int LOWERCASE_T = 116;
    final int UPPERCASE_T = 84;
    public void init()
    {
        button1 = new Button("Test Button");
        add(button1);
        str = "TEST COLOR";
        color = Color.black;
        resize(400, 200);
    }
    public void paint(Graphics g)
    {
        Font font = new Font("TimesRoman", Font.PLAIN, 48);
        g.setFont(font);
        g.setColor(color);
        g.drawString(str, 55, 120);
    }
    public boolean action(Event evt, Object arg)
    {
        if (arg == "Test Button")
        {
            if (color == Color.black)
                color = Color.red;
            else
                color = Color.black;
            repaint();
            return true;
        }
        return false;
    }
    public boolean keyDown(Event evt, int key)
    {
        if ((key == LOWERCASE_T) || (key == UPPERCASE_T))
        {
            Event event = new Event(button1,
                Event.ACTION_EVENT, "Test Button");
            deliverEvent(event);
            return true;
        }
        return false;
    }
}

FIG. 19.12
EventApplet
creates and delivers its own events.

New Event-Handling Techniques for Java 1.1

In the previous sections, you learned that in the Java 1.0 event model, the Component class is where most event-handling occurs. This means that any Java 1.0 class that needs to handle events has to be able to trace its ancestry back to the Component class. Java 1.1 revises the event model so that any class can receive and manage events, regardless of whether that class has Component as a superclass.

In Java 1.1, events are managed by event listeners. Event listeners are classes that have been registered with the Java system to receive specific events. Only the types of events that are registered with an event listener will be received by the listener.

In this section, you modify the EventApplet applet so that it uses the Java 1.1 event model. The original version of the program, developed in the previous section, used the more familiar Java 1.0 event model.

The first step in changing EventApplet into EventApplet2 is to add the following line to the top of the source-code file:

import java.awt.event.*;

This line gives the program access to the new classes defined in the event package. If you fail to include this package, a program using the Java 1.1 event model will not compile.

Next, you must determine which events your program handles and which components generate those events. To keep things simple, you write EventApplet2 so that it responds only to action events, which are the most common events handled in an applet. Table 19.7 provides the information you need to convert any applet to the Java 1.1 event model. The table lists all the events, showing which components generate those events and which interface to use for the Java 1.1 event model.
Table 19.7 Summary of the Java 1.1 Event Model
Event Components Interface
ACTION_EVENT Button, List, MenuItem ActionListener
ACTION_EVENT CheckBox, Choice ItemListener
ACTION_EVENT TextField ActionListener
GOT_FOCUS Component FocusListener
KEY_ACTION Component KeyListener
KEY_ACTION_RELEASE Component KeyListener
KEY_PRESS Component KeyListener
KEY_RELEASE Component KeyListener
LIST_DESELECT Checkbox, CheckboxMenuItem ItemListener
LIST_DESELECT Choice, List ItemListener
LIST_SELECT Checkbox, CheckboxMenuItem ItemListener
LIST_SELECT Choice, List ItemListener
LOST_FOCUS Component FocusListener
MOUSE_DOWN Canvas, Dialog, Frame MouseListener
MOUSE_DOWN Panel, Window MouseListener
MOUSE_DRAG Canvas, Dialog, Frame MouseMotionListener
MOUSE_DRAG Panel, Window MouseMotionListener
MOUSE_ENTER Canvas, Dialog, Frame MouseListener
MOUSE_ENTER Panel, Window MouseListener
MOUSE_EXIT Canvas, Dialog, Frame MouseListener
MOUSE_EXIT Panel, Window MouseListener
MOUSE_MOVE Canvas, Dialog, Frame MouseMotionListener
MOUSE_MOVE Panel, Window MouseMotionListener
MOUSE_UP Canvas, Dialog, Frame MouseListener
MOUSE_UP Panel, Window MouseListener
SCROLL_ABSOLUTE Scrollbar AdjustmentListener
SCROLL_BEGIN Scrollbar AdjustmentListener
SCROLL_END Scrollbar AdjustmentListener
SCROLL_LINE_DOWN Scrollbar AdjustmentListener
SCROLL_LINE_UP Scrollbar AdjustmentListener
SCROLL_PAGE_DOWN Scrollbar AdjustmentListener
SCROLL_PAGE_UP Scrollbar AdjustmentListener
WINDOW_DEICONIFY Dialog, Frame WindowListener
WINDOW_DESTROY Dialog, Frame WindowListener
WINDOW_EXPOSE Dialog, Frame WindowListener
WINDOW_ICONIFY Dialog, Frame WindowListener
WINDOW_MOVED Dialog, Frame ComponentListener
So, comparing the EventApplet program to Table 19.2, you can see that the ACTION_EVENT produced by the Button component should be handled by the ActionListener interface. This means that you must declare EventApplet2 as implementing the ActionListener interface, like this:

public class EventApplet2 extends Applet
    implements ActionListener

Now, in order to receive events from the Button component, the program must create the button, register the button as an ActionListener, and add the button to the applet. All this is done in the init() method, like this:


button1 = new Button("Test Button");
button1.addActionListener(this);
add(button1);

As you can see, the Button class now has a method called addActionListener() that registers the button as an ActionListener. Other components have similar new methods. For example, the Scrollbar class now has a method called addAdjustmentListener() that registers the scrollbar as an AdjustmentListener.

Because the applet implements the ActionListener interface, it must also implement every method declared in the interface. Luckily, ActionListener declares only a single method--actionPerformed(). This method replaces the old action() method as the place where the program handles the action events. Listing 19.16 shows the old action() method, whereas Listing 19.17 shows the new actionPerformed() method.

Listing 19.16LST19_16.TXT--The Old action() Method

public boolean action(Event evt, Object arg)
{
    if (arg == "Test Button")
    {
        if (color == Color.black)
            color = Color.red;
        else
            color = Color.black;
        repaint();
        return true;
    }
    return false;
}

Listing 19.17LST19_17.TXT--The New actionPerformed() Method

public void actionPerformed(ActionEvent event)
{
    String arg = event.getActionCommand();
    if (arg == "Test Button")
    {
        if (color == Color.black)
            color = Color.red;
        else
            color = Color.black;
        repaint();
    }
}

If you examine the two listings closely, you discover that there are really only two main differences. First, action() returns a boolean value, whereas actionPerformed() returns no value. Second, the arg variable that holds the button's text label is passed to action() as a parameter, whereas in actionPerformed(), you get the text label by calling the ActionEvent object's getActionCommand() method. Listing 19.18 shows the complete EventApplet2, which handles its button component using the Java 1.1 event model.

Listing 19.18EventApplet2.java--An Applet that Incorporates the Java 1.1 Event Model

import java.awt.event.*;
import java.awt.*;
import java.applet.*;

public class EventApplet2 extends Applet
    implements ActionListener
{
    Button button1;
    Color color;

    public void init()
    {
        button1 = new Button("Test Button");
        button1.addActionListener(this);
        add(button1);
        color = Color.black;
        resize(400, 200);
    }

    public void paint(Graphics g)
    {
        Font font = new Font("TimesRoman", Font.PLAIN, 48);
        g.setFont(font);
        g.setColor(color);
        g.drawString("TEST COLOR", 55, 120);
    }

    //************************************************
    // Here is the method implementation for the
    // ActionListener interface.
    //************************************************

    public void actionPerformed(ActionEvent event)
    {
        String arg = event.getActionCommand();
        if (arg == "Test Button")
        {
            if (color == Color.black)
                color = Color.red;
            else
                color = Color.black;
            repaint();
        }
    }
}


NOTE: To determine what methods your Java program must implement for a listener interface, load the interface's source code and copy the method declarations into your own program. Then, finish implementing the methods by writing code for the methods you need and changing the remaining declarations into empty methods.