In this chapter, you learn how to write exception-handlers to protect your code from errors. Although you cannot guarantee that you'll think of all exceptions, your exceptions can handle expected problems that often arise when you perform complicated calculations or work with files in your Java applications.
In addition to exceptions, you'll find a brief introduction to threaded programming. The threads of a program are execution paths that make your users think a program can do multiple tasks at the same time. Although threaded programming does not increase your program's speed (actually, threaded programming slows down a program's total execution time somewhat), the user will be more pleased because a threaded program makes the user feel as if the program is more active.
Have you heard of the phrase management by exception? The MBE management style is designed for employees who need little supervision to complete their jobs. Generally, the employees who work best under MBE are those who fall into one of the following categories: self-starters, loners, star performers, and quick learners. Instead of wasting time managing these kinds of employees, a manager can spend time managing other parts of the business that need more than routine attention.
The only time MBE employees need management is when an exception occurs from the projected work pattern. That exception might be good or bad. For example, if an employee routinely sells a target of 20,000 widgets every month, and one month goes by where that employee sold only 3,000 widgets, that employee certainly needs management's attention. Management needs to make sure that the shortcoming is reasonable and the employee will be back on track the next month.
Good exceptions should flag management's attention as well as bad exceptions. If an employee's typical sales figures grow from 20,000 widgets to over 1 million units in a month, management must look into the good exception as well. That employee may have wrongly cut prices below wholesale, the accounting department may have made a bookkeeping error in their recording of sales, or the employee may have landed an international contract to supply all of Italythat employee needs to be praised (such as, being made Supreme Ruler of the company, or something).
As you can see, MBE appears only when things don't go as expected. When things deviate from the normal flow, an exception triggers management's appearance. Management wants to keep things running smoothly and bad as well as good exceptions need to be analyzed for their impact on the future.
The concept of MBE are virtually identical in concept to exceptions that you work with in Visual J++ programs. Now that you've had a rundown of MBE you can better understand what a Visual J++ program looks for when you add exception code to it. Visual J++ can use exceptions to manage a program's execution. A Visual J++ exception occurs when something happens that Visual J++ was not expecting; most usually, a runtime bug.
Exception handling refers to a Visual J++ program that manages certain exceptions. Just as you cannot always predict every bug that can occur, you cannot always predict the exceptions that you need to handle. Generally, if you find yourself adding extensive error-detecting code to certain actions such as files that your program expects but cannot find, and your error-detection code handles the situation (such as informing the user about the problem), that situation deserves an exception-handling process.
When you set up a Visual J++ with exception handling, you tell the program to throw an exception if the exception situation occurs. In other words, when something goes unexpectedly wrong and you've set up exception code, that error will throw an exception. Other code must catch the exception, meaning that the catching code will take over if and only if the exception (such as an error) takes place. The idea is that the catching code will handle the problem seamlessly without runtime errors entering into the foray and onto the user's screen.
Whenever you want Visual J++ to throw an exception that you can handle, add the throws keyword to a method's definition. The following statement defines a method without exception-handling code:
public String getAppletInfo()
{ // Body of method goes here
Here is the same method definition with exception-handling code:
public String getAppletInfo() throws exceptionName
{ // Body of method goes here
Not only does this method's definition include the throws keyword, but the method tells the name of the exception that might be thrown: exceptionName. When you call this method from any other location in the program, the throws keyword tells the system that you will handle the exception.
Some Exceptions Are Automatic
Table 15.1 The Pre-Defined Exceptions
Class package | Exceptions |
Java.awt | AWTException |
Java.io | EOFException, FileNotFoundException, InterruptedIOException, IOException, UTFDataFormatException |
Java.lang | ArithmeticException, ArrayIndexOutOfBoundsException, ArrayStoreException, ClassCastException, ClassNotFoundException, CloneNotSupportedException, Exception, IllegalAccessException, IllegalArgumentException, IllegalMonitorException, IllegalThreadStateException, IndexOutOfBoundsException, InstantiationException, InterruptedException, NegativeArraySizeException, NoSuchMethodException, NullPointerException, NumberFormatException, RuntimeException, SecurityException, StringIndexOutOfBoundsException |
Java.net | MalformedURLException, ProtocolException, SocketException, UnknownHostException, UnknownServiceException |
Java.util | EmptyStackException, NoSuchElementException |
What exceptions are thrown? Those you've defined inside your method definition lines with throws and those pre-defined in classes such as Java.lang. When an exception is thrown (when it occurs due to an error or another defined exception trigger), your program will not handle the exception unless you place the exception-causing code inside a try block. A try block is a block of code that begins with the try keyword, such as the following:
try
{ avg = CalculateAvg(); // Computes a divided average
g.drawString("The average is " + avg); // May not execute!
}
In other words, if the average calculation triggers a mathematical error and you've defined the error as an exception using throws or the error is pre-defined, the exception will be handled by your exception handling code. The Java.lang class package defines an exception named ArithmeticException that will be thrown if your running code ever produces an arithmetic error as long as the error occurs in a try block. (The pre-defined nature of ArithmeticException means that you don't have to code it inside the method's definition line with throws.) If, however, another part of this method triggers a mathematical error and that error code does not appear inside a try block, a runtime error will occur. The bottom line is that your exceptions will be runtime errors unless the code that triggers the exception appears in a try block. Therefore, if you are about to write a part of a program that can trigger a runtime error, place that code inside a try block. Instead of an error, if one occurs, your exception-handling code will execute.
The try block has tremendous power that's not always obvious from simple examples. For instance, as long as a try block is still in effect, even if the try block calls other methods that in turn call additional methods, the exception handling will be active. No matter how much work the try block performs and no matter how many levels of method calls the try block executes, the try block's exception handling remains active. Only when the program's execution flies past the try block's closing brace does the exception handler deactivate.
Place the exception-handling code inside a catch block. A catch block handles a particular exception. In other words, you must code a catch block for each exception your program might try to throw. Here is a possible catch block you could write for the ArithmeticException exception:
catch(ArithmeticException excep)
{ g.drawString("An error occurred when you calculated
[ic:ccc]the average.");
g.drawString("Run payroll's pre-process before this
[ic:ccc]application.");
} // The exception will not produce a runtime error now
Of course, some try blocks might throw one of several possible exceptions. Write as many catch blocks as you need to handle all exceptions that may occur. You'll generally place the catch blocks immediately after the try block. If any try block exception appears in the catch blocks, the system will execute your catch block instead of generating an error that would otherwise occur.
The following code shows three possible exception-handling try blocks:
catch(ClassNotFoundException excep)
{ handleClassNotFound(excep); }
catch(NullPointerException excep)
{ handleNullPointer(excep); }
catch(FileNotFoundException excep)
{ handleFileNotFound (excep); }
Visual J++ analyzes the catch blocks sequentially so if you list multiple catch blocks, the first catch block will catch all the exceptions that it can and, failing to do so, the second catch block will attempt to do the same. You can code a catch-all Throwable catch block such a this one:
catch (Throwable excep_
{
// Handle whatever exception is thrown
}
Keep in mind, however, that such a universal catch block will not let any runtime errors appear, even dangerous unexpected errors that you should let go through such as hardware failures.
Exceptions will always handle expected problems and the catch block will not allow the rest of an exception-throwing method to continue. If, however, a method contains both an exception-handling try block as well as code that must execute no matter what happens with the exception, use a finally block. An exception always returns to execute a finally block, indicated with the finally keyword, no matter what the exception-handling code does. Here is a finally block:
finally
{
// This code ALWAYS executes.
// Assume that try and catch blocks
// appeared earlier in this method.
}
The throw keyword, as opposed to throws that appears in a method definition that defines an exception, lets you trigger an exception that otherwise would not occur. For example, suppose you write a method that receives an array's index value (the array's subscript) and your method is to print the array's value for that index. If the index comes to the method but the index is negative or higher than the array boundary, your method cannot do anything with the index. You can choose to print a message or print a default array element (such as the first element) or you can choose to trigger an exception. The Java.lang class package includes pre-defined exceptions called ArrayIndexOutOfBoundsException and NegativeArraySizeException, and those exceptions can be thrown if you place the throwing code that uses the passed index inside a try block. You can code an if statement at the top of the method and throw the exception yourself like this:
void prArrayElement(int index)
{
if (index <= 0)
{ throw new ArrayIndexOutOfBoundsException(); }
else if (index >= maxIndex)
{ throw new NegativeArraySizeException(); }
// Rest of method appears here and this code will
// execute as long as the exception does not occur
The calling routine catches the exceptions thrown at it and will need to handle the problems, such as adjusting the passed index or gracefully shutting down the program. You would include the exception-handling methods to override those same exception base methods in the Java.lang class package.
If you want to create your own exceptions and throw those exceptions, as opposed to using the exceptions listed in the pre-defined classes, you can do so using the following format that subclasses the pre-built Exception method:
class MyOwnException extends Exception
{
MyOwnException(String excepMessage)
{
handleMyOwn(excepMessage); // handle the exception
}
One cannot use Visual J++ for very long without running across the term thread, threaded, or multithreaded code. The Java language is threadable, which simply means you can execute one or more execution paths, (the threads) simultaneously. At least, the code appears to run simultaneously. Unless you have a multi-processor computer, most computers can do only one thing at a time. The simultaneously-executed threads, however, let your users watch a graphic picture paint on the screen while another thread, such as a long sorting routine, takes place at the same time. The two threads actually share processor time but the user can see things happen (the graphics) while waiting on the sorting instead of staring at a black screen while the sort takes place.
Every program is threaded. It's just that many programs contain only a single thread.
Figure 16.1 shows Visual J++'s New Project Workspace wizard's third dialog box which asks if you want a threaded applet. The spinning globe shows animation as long as you keep the Y es, please option checked to indicate that you want a threaded program. If you check the N o, thank you option, the globe stops spinning. The applet wizard knows that your users will not want to see animation in a non-threaded program because too many things have to happen on a Web page for the animation to halt other operations.
You can request a threaded program from the New Project Wizard.
One of the most troublesome aspects of threaded programming is synchronizing all the threads. If the threads update or use the same variable, for example, you cannot be sure when the variable will update between the running threads. In other words, you must have a way to synchronize the threads so that they execute in a particular order under certain circumstances.
The pre-defined java.lang.Thread class contains the bulk of threaded code you'll use if you want to write a threaded program. As with most pre-defined classes, you'll not directly use java.lang.Thread but you'll inherit, or subclass, from java.lang.Thread to add multithreading to your code. java.lang.Thread contains a run() method that knows virtually everything there is about spawning new threads (when a program spawns a new thread, the thread is defined, created, and begins to execute).
Here is the general code you'll write to subclass java.lang.Thread's run() method:
class aThreadClass extends Thread
{
// Class stuff goes here; you
// can define class data and methods
//
public void run() // This does the threaded work
{
// Your code goes here that you want to thread
}
// Rest of class follows
}
The class does nothing until you create an instance of the class. Therefore, you cannot just execute run() until you've instantiated some aThreadClass objects like this:
aThreadClass firstThread = new aThreadClass(); // Creates a
[ic:ccc]new thread
aThreadClass secondThread = new aThreadClass(); // Creates a
[ic:ccc]new thread
Don't run your thread directly. Instead, use the start() and stop() methods, subclassed automatically from java.lang.Thread, like this:
firstThread.start(); // Executes the first thread's run() method
secondThread.start(); // Executes the second thread's run() method
If you want to stop a threaded process, use stop(). The following statement stops the first thread's execution:
firstThread.stop(); // The first thread halts
The second thread would still continue.
Due to the nature of threading and depending on your program, you could attempt to stop a thread that is not currently running (which triggers the pre-defined exception named IllegalThreadStateException). Therefore, use the isAlive() method to see if a thread is currently running before you stop it like this:
If (firstThread.isAlive()) // Returns true or false
{
firstThread.stop(); // The first thread halts safely
}
// The thread does not need to stop if isAlive() returned false
The start() and stop() methods force a new execution and a complete halt for the thread. If you want to freeze a thread, temporarily, use suspend() and resume(). The following statement suspends but does not stop the first thread:
firstThread.suspend(); // The first thread freezes
When you're ready to resume the thread's execution, use resume() like this:
firstThread.resume(); // The first thread continues
The previous sections were descriptive but naive. You cannot always inherit from Thread. In fact, your applets must directly inherit from Applet so you've got to use an inheritance trick to add threaded programming to your applet. The trick is simple: Use the Runnable keyword.
Consider the following class definition the Visual J++ New Project Wizard writes for you when you design a new threaded class named WebAction:
public class WebAction extends Applet implements Runnable
{
The Runnable keyword tells Visual J++ that the class will be multi-threaded and will contain threaded objects that need java.lang.Thread's run() method (via the start(), stop(), suspend(), and resume() methods described in the previous two sections). You'll now create threaded objects that can run more directly like this:
Thread firstThread = new Thread(); // Creates a new thread
Thread secondThread = new Thread(); // Creates a new thread
Two or more threads might execute in an order that causes confusion. For example, if a thread updates a variable that another thread prints, you need to determine which thread is critical and synchronize that thread's code so that certain statements execute by themselves without two or more threads running the code at the same time. In other words, two or more threads might be able to execute the same code, as is often the case, but you want to ensure that the code executes by one thread only at any one time.
Here is a method definition that uses synchronized (notice the placement of synchronized appears before the method's return data type unlike Runnable):
synchronized int myCriticalMethod(int a, float b)
{ // Only one thread at a time gets into this method
Still have questions about threaded programming? You will because this chapter scratches only the surface. You might want to study the threaded nature of a simple program you create with the New Applet Wizard before diving farther into threads. Threaded programming is a new concept for most programmers, even the programmers who have programmed for years in other languages.
Consider all the advanced threaded topics you can tackle:
Although threaded programming is not an extremely difficult topic, these more advanced capabilities go beyond the scope of this book. Don't miss the forest for the trees; your first priority (stop all other low-priority threads!) should be to learn Visual J++'s environment, practice writing Java-based Visual J++ programs, and discover the more advanced topics, as you're doing now to learn what's ahead for you as a Web site developer and Visual J++ programmer.
The goal of this chapter is to teach you how to add exception-handling to your Visual J++ programs. When you work with exceptions, you help ensure that your user will see few if any runtime bugs and your programs will more gracefully exit from problems.
In addition to exception handling, Visual J++ supports threaded programming which lets your program go off on two or more execution paths at the same time. Your users will be pleased when you write threaded code because the user will not have to wait for one process to finish before seeing the results of another.
The next chapter continues to describe advanced Visual J++ capabilities by explaining how to implement content and protocol handlers. Although the new Visual J++ programmer will not need content or protocol handlers for a while, you should understand what content and protocol handlers are and you should be aware of the programming requirements they generate.
Here are the points this chapter covered:
| 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.