A Java exception is an object that describes an exceptional condition that has occurred in a piece of code.
Categories of exceptions:
Checked
exceptions: A checked
exception is an exception that is typically a user error or a problem that
cannot be foreseen by the programmer. For example, if a file is to be opened,
but the file cannot be found, an exception occurs. These exceptions cannot
simply be ignored at the time of compilation.
Runtime
exceptions: A runtime exception is an
exception that occurs that probably could have been avoided by the programmer.
As opposed to checked exceptions, runtime exceptions are ignored at the time of
compliation.
Errors: These are not exceptions at all, but problems that
arise beyond the control of the user or the programmer. Errors are typically
ignored in your code because you can rarely do anything about an error. For
example, if a stack overflow occurs, an error will arise. They are also ignored
at the time of compilation.
When an exceptional condition arises, an object representing that exception is
created and thrown in the method that caused the error. That method may
choose to handle the exception itself, or pass it on. Either way, at some
point, the exception is caught and processed. Exceptions can be
generated by the Java run-time system, or they can be manually generated by
your code. Exceptions thrown by Java relate to fundamental errors that violate
the rules of the Java language or the constraints of the Java execution
environment. Manually generated exceptions are typically used to report some
error condition to the caller of a method. Java exception handling is managed
via five keywords: try, catch, throw, throws, and finally
Briefly, here is how they work. Program statements that you want to
monitor for exceptions are contained within a try block.
If an exception occurs within the try block,
it is thrown. Your code can catch this exception (using catch)
and handle it in some rational manner. System-generated exceptions are
automatically thrown by the Java run-time system.
To manually throw an exception,
use the keyword throw. Any exception that is thrown
out of a method must be specified as such by a throws
clause. Any code that absolutely must be executed after a try block
completes is put in a finally block.
This is the general form of an exception-handling block:
try
{
//
block of code to monitor for errors
}
catch
(ExceptionType1 exObj)
{
//
exception handler for ExceptionType1
}
catch
(ExceptionType2 exObj)
{
//
exception handler for ExceptionType2
}
//
...
finally
{
//
block of code to be executed after try block ends
}
|
Here, ExceptionType
is the type of exception that has occurred
Exception Types:
All exception
types are subclasses of the built-in class Throwable. Thus, Throwable is at the top
of the exception class hierarchy. Immediately below Throwable are two
subclasses that partition exceptions into two distinct branches. One branch is
headed by Exception. This class is
used for exceptional conditions that user programs should catch. This is also
the class that you will subclass to create your own custom exception types.
There is an important subclass of Exception, called RuntimeException. Exceptions of
this type are automatically defined for the programs that you write and include
things such as division by zero and invalid array indexing.
The
other branch is topped by Error, which defines exceptions that
are not expected to be caught under normal circumstances by your program.
Exceptions of type Error are used by the Java run-time system to indicate errors having
to do with the run-time environment, itself. Stack overflow is an example of
such an error. This chapter will not be dealing with exceptions of type Error, because these
are typically created in response to catastrophic failures that cannot usually
be handled by your program.
try/catch
Statement
The try/catch statement encloses some
code and is used to handle errors and exceptions that might occur in that code.
Here is the general syntax of the try/catch
statement:
try
{
body-code
}
catch
(exception-classname variable-name)
{
handler-code
}
|
The try/catch statement has
four parts. The body-code contains code that might
throw the exception that we want to handle. The exception-classname
is the class name of the exception we want to handle. The variable-name
specifies a name for a variable that will hold the exception object if the
exception occurs. Finally, the handler-code contains
the code to execute if the exception occurs. After the handler-code executes,
execution of the thread continues after the try/catch statement. Here is an example of code that tries to
create a file in a non-existent directory which results in an IOException.
String filename
= "/nosuchdir/myfilename";
try
{
// Create the file
new File(filename).createNewFile();
}
catch
(IOException e)
{
// Print out the exception that occurred
System.out.println("Unable to create "+filename+": "+e.getMessage());
}
// Execution
continues here after the IOException handler is executed
|
Here's
the output:
Unable
to create /nosuchdir/myfilename: The system cannot find the path specified.
Although the default exception handler provided by the Java
run-time system is useful for debugging, you will usually want to handle an
exception yourself. Doing so provides two benefits. First, it allows you to fix
the error. Second, it prevents the program from automatically terminating. Most
users would be confused (to say the least) if your program stopped running and
printed a stack trace whenever an error occurred! It is quite easy to prevent
this.
To guard against and handle a run-time error, simply enclose the code
that you want to monitor inside a try block.
Immediately following the try block,
include a catch clause that specifies the exception type that you
wish to catch.
To illustrate how easily this can
be done, the following program includes a try block
and a catch clause that processes the ArithmeticException
generated by the division-by-zero error:
class
Exc2
{
public
static void main(String args[])
{
int
d, a;
try
{
// monitor a block of code.
d =
0;
a =
42 / d;
System.out.println("This
will not be printed.");
}
catch
(ArithmeticException e)
{
// catch divide-by-zero error
System.out.println("Division
by zero.");
}
System.out.println("After
catch statement.");
}
}
|
This program generates the following output:
Division by zero.
After catch statement.
Notice
that the call to println( ) inside the try block
is never executed. Once an exception is thrown, program control transfers out
of the try block into the catch block.
Put differently, catch is not “called,” so execution
never “returns” to the try block from a catch.
Thus, the line “This will not be printed.” is not displayed. Once the catch
statement has executed, program control continues with the next
line in the program following the entire try/catch mechanism.
A try and
its catch statement form a unit. The scope of the catch
clause is restricted to those statements specified by the immediately
preceding try statement. A catch statement
cannot catch an exception thrown by another try statement
(except in the case of nested try statements,
described shortly). The statements that are protected by try must
be surrounded by curly braces. (That is, they must be within a block.) You
cannot use try on a single statement.
The
goal of most well-constructed catch clauses
should be to resolve the exceptional condition and then continue on as if the
error had never happened. For example, in the next program each iteration of
the for loop obtains two random integers. Those two
integers are divided by each other, and the result is used to divide the value
12345. The final result is put into a.
If either division operation causes a divide-by-zero error, it is caught, the
value of a is set to zero, and the program continues.
//
Handle an exception and move on.
import
java.util.Random;
class
HandleError
{
public
static void main(String args[])
{
int
a=0, b=0, c=0;
Random
r = new Random();
for(int
i=0; i<32000; i++)
{
try
{
b
= r.nextInt();
c
= r.nextInt();
a
= 12345 / (b/c);
}
catch
(ArithmeticException e)
{
System.out.println("Division
by zero.");
a
= 0; // set a to zero and continue
}
System.out.println("a:
" + a);
}
}
}
|
Multiple catch Clauses
In some cases, more than one exception could be
raised by a single piece of code. To handle this type of situation, you can
specify two or more catch clauses, each catching a
different type of exception. When an exception is thrown, each catch
statement is inspected in order, and the first one whose type
matches that of the exception is executed. After one catch statement
executes, the others are bypassed, and execution continues after the try/catch block.
The following example traps two different exception types:
// Demonstrate multiple catch statements.
class MultiCatch
{
public static void main(String args[])
{
Try
{
int a = args.length;
System.out.println("a = " + a);
int b = 42 / a;
int c[] = { 1 };
c[42] = 99;
}
catch(ArithmeticException e)
{
System.out.println("Divide by 0: " + e);
}
catch(ArrayIndexOutOfBoundsException e)
{
System.out.println("Array index oob: " + e);
}
System.out.println("After try/catch
blocks.");
}
}
|
This program will cause a division-by-zero
exception if it is started with no commandline arguments, since a will
equal zero. It will survive the division if you provide a command-line
argument, setting a to something larger than zero.
But
it will cause an ArrayIndexOutOfBoundsException,
since the int array c has
a length of 1, yet the program attempts to assign a value to c[42].
Here is the output generated by running it both
ways:
C:\>java MultiCatch
a = 0
Divide by 0: java.lang.ArithmeticException: / by zero
After try/catch blocks.
C:\>java MultiCatch TestArg
a = 1
Array index oob:
java.lang.ArrayIndexOutOfBoundsException:42
After try/catch blocks.
When you use multiple catch
statements, it is important to remember that exception subclasses
must come before any of their superclasses. This is because a catch
statement that uses a superclass will catch exceptions of that
type plus any of its subclasses. Thus, a subclass would never be reached if it
came after its superclass. Further, in Java, unreachable code is an error.