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.
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
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.
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.
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:
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.
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.
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.
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.
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.)
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.
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.
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.
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:
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.
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:
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.
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. }
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.
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.
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.
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)
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.
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.
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 |
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 |
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 |
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 |
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.
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() |
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
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 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.
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.
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; }
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.
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.
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.
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.
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.
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.
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 |
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.
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 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.
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.