- Special Edition Using Java, 2nd Edition -

Chapter 19

Exceptions and Events in Depth


by Clayton Walnum

When 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.

Another type of object 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 soon see, there are several ways that 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.

Java's 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 non-existent 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 for more information.
 

Listing 19.1 LST19_01.TXT—Handling 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.

The catch program block does more than direct program execution. It actually catches the exception object thrown by Java. For example, 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 fig. 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 fig. 19.2), where the classes and exceptions are listed.


FIG. 19.2

Java's online documentation lists the exception objects that may be thrown by methods in a class.

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 fig. 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.2 LST19_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.3 LST19_03.TXT—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, on 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 as another may be stricken to its digital knees. The best approach is to handle in your program any exceptions that may occur. 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.
ArrayIndexOutOfBoundsException Bad array indexes.
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

.

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.4 LST19_04.TXT—Calling a Throwable object's 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)

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.5 ExceptionApplet.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 Appletviewer, 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 fig. 19.6).


FIG. 19.5

The ExceptionApplet is running under Appletviewer.


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 Appletviewer, 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 NumberFormatException 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 Appletviewer, 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 be 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:

Load ExceptionApplet into your text editor.

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

Listing 19.6 LST19_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 the class declaration line, change the name of the class to ExceptionApplet2.

Save the new applet under the name ExceptionApplet2.java.

Load the EXCEPTIONAPPLET.HTML file.

Change all occurrences of ExceptionApplet to ExceptionApplet2.

Save the file as EXCEPTIONAPPLET2.HTML.

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 NumberFormatException 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:

Load ExceptionApplet2 into your text editor.

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

Listing 19.7 LST19_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;
}

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

Save the new applet under the name ExceptionApplet3.java.

Load the EXCEPTIONAPPLET.HTML file.

Change all occurrences of ExceptionApplet to ExceptionApplet3.

Save the file as EXCEPTIONAPPLET3.HTML.

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.

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.
 

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.8 LST19_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.9 NumberRangeException.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.

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.10 ExceptionApplet4.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 NumberRangeException 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 coverts 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.

Java's Error Classes

So far, you've had a look at the exception classes that 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)

Java's 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 16, "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.

See "Events," Chapter 16 for more information.
 

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 deselected 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.
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 Event's 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 system 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 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 Appletviewer.

See "Events," Chapter 16 for more information.
 

Listing 19.11 MouseApplet.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 16, "Advanced Applet Code." In this section, you learn even more about how to deal with the keyboard in your Java programs.

See "Events," Chapter 16 for more information.
 

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 key's 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 which of 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 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.12 LST19_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.13 DrawApplet2.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;
}
}
}

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 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.14 LST19x_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 be 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 Appletviewer.

Listing 19.15 EventApplet.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.


Previous Page TOC Next Page

| Previous Chapter | Next Chapter |

| Table of Contents | Book Home Page |

| Que Home Page | Digital Bookshelf | Disclaimer |


To order books from QUE, call us at 800-716-0044 or 317-361-5400.

For comments or technical support for our books and software, select Talk to Us.

© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company.