Errors and Exceptions
3. Throw (using reserved word throw ) the exception object.
All exception classes provide constructors allowing the user to initialize a string parameter, message (there is a default message), which describes the problem that caused the exception. This message string may be used to provide useful information to the user. An example is:
throw new EOFException("End of file error occurred in method getData" );
Once a method throws an exception the method does not return to its caller. Control is transferred to the exception handler, if any (see Section 7.5). If no exception handler has been defined, program execution terminates if the application is a non-GUI application and displays the message in a console window if the application is a GUI application. GUI applications continue running in the presence of an exception (although not always properly). When debugging a GUI application it is advisable to keep a console window open to detect exceptions.
If you redefine a method from a superclass in your subclass, the subclass method cannot throw more checked exceptions than the superclass method that you redefine. If no checked exceptions are thrown by the superclass method, then the subclass must throw none. This is consistent with the constraint that a polymorphically redefined method in a subclass must have exactly the same signature that it has in the parent class.
When a method declares that it throws an exception that is an instance of a particular checked exception class, it may throw an exception of that class or any of its descendent classes. Declaring it to throw Exception allows an instance of any exception subclass to be thrown. Declaring it to throw a specific exception subclass provides more precise information on what the exception is.
7.4—
Creating Exception Classes
A method that you write may need an exception object not available in the standard Exception classes. You must extend your exception class from one in the Exception hierarchy. A highly contrived example is shown in Listing 7.2, which throws a SpecialFileException on encountering a 'Z' character while reading a file. Notice that the only thing unique about this exception class is its name; this is typical of all exception and error classes. In essence we are cataloging exceptions and errors by unique class names and the value of their contained message strings.
Listing 7.2 Creating a Custom Exception Class – Selected Details
class SpecialFileException extends IOException {
// Thrown when reading letter 'Z' in a file // Constructors
public SpecialFileException (String message) {
super(message); }
}
class SpecialFileReader {
public String readSpecialData (BufferedReader in) throws
SpecialFileException {
// Throws exception if file contains letter 'Z' . . .
while ( . . .) {
if (ch == 'Z') // character 'Z' encountered
throw new SpecialFileException(''Z encountered" ); . . . } . . . } } 7.5— Handling Exceptions
Any method that can receive an exception from one of its statements may choose to handle the exception rather than pass it along with a throws clause (required only for checked exceptions). The method may choose to handle both checked and unchecked exceptions. Exception handling in Java is done with try/catch blocks. The structure of these blocks is:
try {
code that may generate an exception }
catch (ExceptionType1 ex) {
code that handles an exception of ExceptionType1 }
catch (ExceptionType2 ex) {
code that handles an exception of ExceptionType2 }
. . .
If any statement inside the try block throws an exception, the remainder of the try block is skipped. Control is transferred to the catch block that handles the specific exception. If none of the catch blocks handle the type of exception generated, the method containing the try/catch blocks exits and control goes to the default exception handler. If no statement in the
If you declare the ExceptionType to be Exception, any exception will be trapped since all exception classes are derived from class Exception. When using multiple catch clauses, it is not advisable to use exception types that are hierarchically related. The result may be a compiler error or cause one or more catch clauses to be unreachable.
In general if the code of your method invokes (calls) one or more functions that throws a checked exception, you must handle the exception with try/catch blocks or pass the exception on by advertising that your method throws that type of exception. The choice of whether to handle or throw an exception is best made on an individual basis. A checked exception may be passed all the way to the runtime system using the throws clause at each calling level (as done in Listing 7.1). The default handling mechanism is to halt and display a default error message. Although not required, the programmer may optionally choose to handle unchecked exceptions as well by using a try/catch block or ignore them and let the runtime use its default-handling mechanism.
7.6—
The Finally Clause
When the code in a method throws an exception it does not execute the remaining code in the try block. Since the try
block did not finish its work, some resources such as opened files or graphics contexts may need to be cleaned up. The optional finally clause allows this to be done.
An example is the following:
Graphics g = image.getGraphics();
try {
Some code that might throw exceptions }
catch (IOException ex) { Exception handling code }
finally {
g.dispose(); }
The g.dispose() will be executed after the exception-handling code. You can have a finally clause without a catch clause. In this case if an exception occurs in the try block, all the code in the finally block will be executed before the program terminates.
7.7—
Putting It All Together – An Example
Listing 7.3 presents details of class ExceptionGenerator, designed to illustrate some of the properties of exceptions. This class contains methods that generate several different kinds of standard exceptions in Java. Some are checked and some are unchecked.
Listing 7.3 Class ExceptionGenerator
/** Generates various kinds of exceptions /
import java.io.*;
public class ExceptionGenerator {
// Fields
Counter myCounter; // Commands
public void incrementCounter () {
// Generate a NullPointerException - runtime myCounter.increment();
}
public void divideByZero () {
// Generate an ArithmeticException - runtime int y = 6, x = 0;
int z = y / x;
System.out.println (''z = " + z); }
public void arrayOutOfBounds () {
// Generate an ArrayIndexOutOfBoundsException - runtime int [] data = new int [10];
data[10] = 16;
System.out.println ("data[10] = " + data[10]); }
public void badCast () throws IOException {
// Generate a NumberFormatException - runtime BufferedReader keyboard = new BufferedReader (
new InputStreamReader (System.in)); System.out.print ("Enter an integer: " );
String line = keyboard.readLine();
int value = (new Integer(line)).intValue(); System.out.println("value = " + value); }
public void numericInput () {
try {
// Have user input a string representing a number BufferedReader keyboard = new BufferedReader (
new InputStreamReader(System.in)); System.out.print ("Enter an integer: " );
String line = keyboard.readLine();
System.out.println("value = " + value); }
catch (IOException ex) { System.out.println (
''IO exception in reading user input." ); }
catch (NumberFormatException ex) {
System.out.println ("Invalid integer entered." ); }
}
public void numericInputWithException () {
try {
// Have user input a string representing a number BufferedReader keyboard = new BufferedReader ( new InputStreamReader (System.in)); System.out.print ("Enter an integer: " ); String line = keyboard.readLine();
int value = (new Integer(line)).intValue(); System.out.println("value = " + value); }
catch (IOException ex) { System.out.println (
"IO exception in reading user input." ); }
} }
Method incrementCounter attempts to send a message to an uninitialized object, myCounter, causing a
NullPointerException (unchecked) to be thrown. Method divideByZero throws an ArithmeticException (unchecked) to be thrown. Method arrayOutOfBounds throws an ArrayIndexOutOfBoundsException (unchecked) when trying to access an invalid index.
Method badCast can throw an IOException (checked) for serious I/O failure or a NumberFormatException (unchecked) for an incorrectly entered number (must be a valid integer string).
Method numericInput also invokes methods that can throw an IOException (checked) or a NumberFormatException
(unchecked). It chooses to handle both exceptions by displaying a custom error message in the console.
Method numericInputWithException chooses to handle the potential IOException but not the NumberFormatException. This choice eliminates the need for a throws clause and places any exception handling for number format errors on the user.
Listing 7.4 presents an application class that allows the user to select and generate the various exceptions in class
Listing 7.4 Class ExceptionsApp
/** Illustrates exception handling */
import java.io.*;
public class ExceptionsApp {
// Fields
ExceptionGenerator except = new ExceptionGenerator(); int choice;
BufferedReader keyboard = new BufferedReader (
new InputStreamReader(System.in)); // Commands
public void generateExceptions () {
System.out.println (''1 --> Null pointer" ); System.out.println ("2 --> Divide by zero" );
System.out.println ("3 --> Array index range error" ); System.out.println ("4 --> Bad cast" );
System.out.println ("5 --> Bad numeric input" ); System.out.println ("6 --> Trap bad numeric input" ); System.out.print("Enter choice: " );
try {
choice = (new Integer(keyboard.readLine())).intValue(); }
catch (IOException ex) { // do nothing } switch (choice) { case 1: except.incrementCounter(); break; case 2: except.divideByZero(); break; case 3: except.arrayOutOfBounds(); break; case 4: try { except.badCast(); }
catch (IOException ex) {} // Unhandled exception break;
case 5:
except.numericInput (); break;
case 6: try {
except.numericInputWithException(); }
catch (NumberFormatException ex) { try {
// Second and final chance
except.numericInputWithException (); }
catch (Exception e) {} }
finally {
System.out.println (''In finally block." ); }
break; }
}
public static void main (String[] args) {
ExceptionsApp app = new ExceptionsApp (); app.generateExceptions();
} }
Notice that, for case 6, in function main, we choose to handle the NumberFormatException that may be generated by method numericInputWithException by allowing the user one additional chance to enter a correct integer. Statements in the finally block are executed after a correct input (valid number) or after the second chance is completed (for an invalid numeric input).
The switch statement in Listing 7.4 allows the user to experiment with each of the exception conditions in class
ExceptionGenerator. After each exception, the program terminates so the user may wish to run the program several times.
7.8—
Catching Runtime Exceptions – An Example
Instances of RuntimeException and its subclasses are unchecked exceptions. Recall that unchecked exceptions always have a default exception handling mechanism in the runtime system without the need for a throws statement. The user is not required to catch or advertise these exceptions. However, the user does have the option to catch and handle the exceptions. This option was exercised in the numericInputWithException option in ExceptionsApp. As a further
illustration we examine the difference between the default exception handler and a custom exception handler in the next two listings. Class StringIndex shown in Listing 7.5 uses the default exception handler and verifies that execution stops at the point of occurrence of an exception.
Listing 7.5 Class StringIndex
/** Unchecked exception - no throws statement required in signature * Default exception handler
*/
class StringIndex {
static public void main (String args[]) {
String myString = new String(''Hello" );
System.out.println("myString[5] is: " + myString.charAt(5) ); System.out.println("Execution stops on exception" );
} }
The main function in class StringIndex attempts to access index 5 of myString, which has index values from 0 to 4. A
StringIndexOutOfBoundsException in package java.lang is thrown and handled by the runtime system as indicated below.
On running the program, we get the following output in the console window. The program terminates.
java.lang.StringIndexOutOfBoundsException: String index out of range: 5 Application Exit . . .
The user may optionally choose to catch and handle the exception to provide customized information about its source. The example shown in Listing 7.6 illustrates this option. The program terminates with the indicated output(in the console window) when the exception occurs.
Listing 7.6 Class StringIndex class StringIndex2 {
static public void main (String args[]) {
String myString = new String("Hello" ); try {
System.out.println("myString[5] is: "
+ myString.charAt(5) ); }
catch (java.lang.StringIndexOutOfBoundsException ex ) { System.out.println("Exception: (index = " + index + ") Max index in myString is: " + (myString.length() - 1) ); }
} }
// Output in console window
Exception: (index = 5) Max index in myString is: 4
Clearly, the attempt to access index 5 of myString is a programming error that should be corrected instead of adding all the try/catch code to the example. How then can we justify catching exceptions generated by programming errors? If the block of code were large, with a complex algorithm for calculating the value of index, a try/catch block could be useful while testing the program. Of course, a good debugger could do the same thing without cluttering the source code. As a general rule, one should use exception-handling code sparingly.
7.9— Summary
Key points to remember about errors and exceptions include the following.
• Errors and exceptions may occur because of hardware problems, software glitches, or misuse of a program and may cause a program to crash with dire effects.
• Languages that support exception handling allow the software developer to catch and handle exceptions in a graceful way. Java provides a rich hierarchy of exception and error classes under class Throwable.
• Exceptions in Java come in two flavors – checked and unchecked. Checked exceptions that can be thrown within a calling method require that the calling method either advertise (by passing responsibility up the calling chain) the exception using a throws clause in its method header or handle the exception using a try/catch clause. There is no need to advertise errors or unchecked exceptions. Unchecked exceptions may be optionally handled with a try/catch clause. Errors should not be handled since they represent serious irrecoverable conditions. The best you can do is display a custom message in the console window. The runtime system has a default handling mechanism for all exceptions and errors.
• The optional finally part of a try/catch clause provides a way to clean up unfinished business as a result of an exception being thrown.
7.10—