C SC 160 Chapter 8 topic: Exceptions
major resource: An Introduction to Object-Oriented Programming with Java, fourth
edition,
Wu, McGraw Hill, 2006
[ previous
| schedule
| next ]
Overview
- An exception is an error condition that occurs during execution (i.e. runtime error)
- Java includes classes and language constructs that empower a program to detect and handle exceptions
- Exceptions are represented by objects (no big surprise)
- Default behavior when exception occurs is to print a stack trace and terminate
- You can intercept exceptions and handle them using the try-catch construct.
- You can propagate (relay) an exception using a throws clause in method header
- You can generate exceptions yourself using the throw statement.
Stack trace
- The stack trace represents the current execution context.
- The term "stack" refers to "runtime stack" with currently executing method on top
- When an exception occurs, the exception message is printed followed by the stack trace
- first line names the method that was running when exception occurred
- every line thereafter names the method that called the method named in the line immediately above it.
- every line also includes the source code line number where the call occurred
- the last line is main()
Intercepting and handling an exception
Here is a method intended to get integer age input from user:
public int getAge() {
String input = JOptionPane.showInputDialog(null, "Please enter your age");
int age = Integer.parseInt(input);
return age;
}
Suppose the user enters non-numeric data by mistake, such as "we" instead of "23" (one keyboard row too
low)? The call to Integer.parseInt(input) will result in an exception and program will
terminate. Not very friendly.
Here is a version that will catch this exception and repeat the request until a valid integer
is entered:
public int getAge() {
String input;
int age;
input = JOptionPane.showInputDialog(null, "Please enter your age");
while (true) {
try {
age = Integer.parseInt(input);
return age;
} catch (NumberFormatException e) {
input = JOptionPane.showInputDialog(null,
"'"+input+"' is not a valid age. Please re-enter.");
}
}
}
The exception strategy works like this:
- The try block contains the statements for which we will detect exceptions.
- If an exception occurs while in the try block, execution flow transfers
immediately to the catch block.
- The catch block receives the exception object as a parameter. I didn't use it here, but it has accessor methods.
- When the catch block finishes, execution control will continue at the next
statement below the catch.
- Note: execution control does not resume from the point where the
exception occurred!
- If an exception does not occur, the catch block is skipped over.
If the exception occurs, the user is prompted to re-enter then control goes to the next
loop iteration to convert again.
Programmer-generated Exceptions
This solution can be improved further by checking to be sure the age is not negative. Here are three
alternatives, two of which use a programmer-generated exception with the throw statement:
- Replace return age; with an if-then-else that tests (age >= 0). If true,
then return age, else do a JOptionPane.showInputDialog() with the error message and to get
another value.
- Just before return age;, add an if-then statement that tests
(age < 0). If true, then throw new Exception();. In addition, change the catch
parameter to be of type Exception so it will catch either the system-generated
NumberFormatException or the programmer-generated exception (Exception is a superclass of
NumberFormatException).
- (Best Solution) Code the if-then as described in alternative 2.
Then add a second catch block having parameter of type Exception
to handle the programmer-generated exception.
The third solution would look something like this, with additional code in bold:
public int getAge() {
String input;
int age;
input = JOptionPane.showInputDialog(null, "Please enter your age");
while (true) {
try {
age = Integer.parseInt(input);
if (age < 0) {
throw new Exception();
}
return age;
} catch (NumberFormatException e) {
input = JOptionPane.showInputDialog(null,
"'"+input+"' is not a valid age. Please re-enter.");
} catch (Exception e) {
input = JOptionPane.showInputDialog(null,
"Age cannot be negative. Please re-enter.");
}
}
}
Additional information about this example:
- As can be seen here, it is possible to "chain together" multiple catch blocks,
each designed to catch a different kind of exception.
- If an exception occurs, an exception object is created and then the first
catch block with a type-compatible parameter will be executed.
- This means if you have multiple catch block, order them from "specific"
to "general" exception type, based on superclass-subclass relationship.
It is also possible to define your own custom Exception subclass then create, throw and catch
exceptions of that type.
Propagating an exception with the throws clause
You may wish to have your method avoid dealing with an exception and "pass the buck" on to the
client method that called it. This is done by adding a throws clause to the method
signature. Here's our same example, with throws added to original version:
public int getAge() throws NumberFormatException {
String input = JOptionPane.showInputDialog(null, "Please enter your age");
int age = Integer.parseInt(input);
return age;
}
See how much easier it is to pass the buck?
- If the exception occurs, the exception object is created
here but is "passed on" to the method that called this one!
- The caller will either handle it or pass it on again,
like a "hot potato"!
- The exception is passed from method to method on the call stack until it comes to one
that will handle it.
- If none will, you get the familiar default behavior.
Checked and Unchecked Exceptions
Java Exceptions are classified as either checked or unchecked
- This refers to whether or not the compiler checks to make sure a method catches or explicitly propagates the exception
- The class RuntimeException and all its subclasses are unchecked.
- The client
of a method that throws one of these is not required by the compiler to do anything about it.
- The most common unchecked exceptions are NumberFormatException, NullPointerException,
ArithmeticException, IndexOutOfBoundsException
- These are exceptions that can occur almost anywhere, so requiring the client to catch/propagate
them is too much to ask, and leads to code that is difficult to follow
- All other exceptions are checked, and the compiler enforces requirement
that client explicitly catch or propagate.
- The most common checked exceptions come from class IOException and all its subclasses.
- You have to deal with IOException and its subclasses when doing file input and output.
Example of using method that throws checked exception
The file input/output methods nearly all throw a checked exception of some sort. If you do file programming,
you will have to deal with this by either catching or explicitly propagating the exception.
This method returns the value read by a FileReader object. The read() method it
inherits from InputStreamReader is
defined to throw IOException, which is a checked exception. Here, we choose to propagate the
exception.
public int getValue(FileReader myReader) throws IOException {
return myReader.read();
}
In the second example, we choose instead to catch the exception and return a -1 value. The client may
interpret this as an "end of stream" and stop reading from the file. In this example it is actually better to
propagate, so the client will know that an error actually occurred rather than think the end of file
was reached.
public int getValue(FileReader myReader) {
try {
return myReader.read();
} catch (IOException e) {
return -1;
}
}
Deciding whether to catch, explicitly propagate, or ignore the exception
Decision of what to do with an exception depends on whether it is checked or unchecked, and the "contract" between a
method and its clients.
Consider your method's rights and responsibilities in its role as a supplier
of a service (server):
- If your method has "published" pre-conditions, the client is responsible for any exceptions
that occur in your method as a result of a unmet pre-condition. Thus you can propagate the exception.
- If your method has "published" the fact that it will throw certain exceptions, then
you can propagate those exceptions.
- Your method should not propagate exceptions other than the ones above.
Consider your method's responsibilities in its role as a client of other methods:
- Determine whether the method you want to use "publishes" the fact that it can throw an exception
- Look at its source code or Method Detail documentation by presence of throws clause
- If there is a throws clause, determine whether the exception type is checked or unchecked (look
at documentation for exception class to see whether RuntimeException is a superclass. If
so, it is unchecked, otherwise it is checked).
- If you want to call a method that throws a checked exception, your method either has to catch or
propagate the exception
- If you want to call a method that throws an unchecked exception or does not have a throws clause, you do not have to do anything.
- Be aware that if you violate the other method's published pre-conditions, you are responsible for
resulting exceptions.
[ C
SC 160 | Peter
Sanderson | Math Sciences server
| Math Sciences home page
| Otterbein ]
Last updated:
Peter Sanderson (PSanderson@otterbein.edu)