An exception is an anomalous condition that alters or interrupts the flow of execution. Java provides built-in exception handling to deal with such conditions. Exception handling should not be part of normal program flow.
As shown in Figure 7-1, all exceptions and errors inherit from the class Throwable
, which inherits from the class Object
.
Exceptions and errors fall into three categories: checked exceptions, unchecked exceptions, and errors.
throws
clause. This must continue all the way up the calling stack until the exception is handled.
catch
block.
Exception
, and all classes that are subtypes of Exception
, except for RuntimeException
and the subtypes of Runtime
Exception
.
The following is an example of a method that throws a checked exception:
// Method declaration that throws
// an IOException
void
readFile
(
String
filename
)
throws
IOException
{
...
}
RuntimeException
and all subtypes of RuntimeException
.
There are various checked exceptions, unchecked exceptions, and unchecked errors that are part of the standard Java platform. Some are more likely to occur than others.
ClassNotFoundException
IOException
IOException
are EOFException
and FileNotFoundException
.
FileNotFoundException
SQLException
InterruptedException
NoSuchMethodException
CloneNotSupportedException
clone()
is called by an object that is not cloneable.
ArithmeticException
ArrayIndexOutOfBoundsException
ClassCastException
DateTimeException
IllegalArgumentException
IllegalStateException
IndexOutOfBoundsException
NullPointerException
NumberFormatException
UncheckedIOException
AssertionError
ExceptionInInitializeError
VirtualMachineError
OutOfMemoryError
NoClassDefFoundError
StackOverflowError
In Java, error-handling code is cleanly separated from error-generating code. Code that generates the exception is said to “throw” an exception, whereas code that handles the exception is said to “catch” the exception:
// Declare an exception
public
void
methodA
()
throws
IOException
{
...
throw
new
IOException
();
...
}
// Catch an exception
public
void
methodB
()
{
...
/* Call to methodA must be in a try/catch block
** since the exception is a checked exception;
** otherwise methodB could throw the exception */
try
{
methodA
();
}
catch
(
IOException
ioe
)
{
System
.
err
.
println
(
ioe
.
getMessage
());
ioe
.
printStackTrace
();
}
}
To throw an exception, use the keyword throw
. Any checked/unchecked exception and error can be thrown:
if
(
n
==
-
1
)
throw
new
EOFException
();
Thrown exceptions are handled by a Java try, catch, finally
block. The Java interpreter looks for code to handle the exception, first looking in the enclosed block of code, and then propagating up the call stack to main()
if necessary. If the exception is not handled on the main thread (i.e., not the Event Dispatch Thread (EDT)), the program exits and a stack trace is printed:
try
{
method
();
}
catch
(
EOFException
eofe
)
{
eofe
.
printStackTrace
();
}
catch
(
IOException
ioe
)
{
ioe
.
printStackTrace
();
}
finally
{
// cleanup
}
The try-catch
statement includes one try
and one or more catch
blocks.
The try
block contains code that may throw exceptions. All checked exceptions that may be thrown must have a catch
block to handle the exception. If no exceptions are thrown, the try
block terminates normally. A try
block may have zero or more catch
clauses to handle the exceptions.
There cannot be any code between the try
block and any of the catch
blocks (if present) or the finally
block (if present).
The catch
block(s) contain code to handle thrown exceptions, including printing information about the exception to a file, giving users an opportunity to input correct information. Note that catch
blocks should never be empty because such “silencing” results in exceptions being hidden, making errors harder to debug.
A common convention for naming the parameter in the catch
clause is a set of letters representing each of the words in the name of the exception:
catch
(
ArrayIndexOutOfBoundsException
aioobe
)
{
aioobe
.
printStackStrace
();
}
Within a catch
clause, a new exception may also be thrown if necessary.
The order of the catch
clauses in a try/catch
block defines the precedence for catching exceptions. Always begin with the most specific exception that may be thrown and end with the most general.
Exceptions thrown in the try
block are directed to the first catch
clause containing arguments of the same type as the exception object or superclass of that type. The catch
block with the Exception
parameter should always be last in the ordered list.
If none of the parameters for the catch
clauses match the exception thrown, the system will search for the parameter that matches the superclass of the exception.
The try-finally
statement includes one try
and one finally
block.
The finally
block is used for releasing resources when necessary:
public
void
testMethod
()
throws
IOException
{
FileWriter
fileWriter
=
new
FileWriter
(
"\data.txt"
);
try
{
fileWriter
.
write
(
"Information..."
);
}
finally
{
fileWriter
.
close
();
}
}
This block is optional and is only used where needed. When used, it is executed last in a try-finally
block and will always be executed, whether or not the try
block terminates normally. If the finally
block throws an exception, it must be handled.
The try-catch-finally
statement includes one try
, one or more catch
blocks, and one finally
block.
For this statement, the finally
block is also used for cleanup and releasing resources:
public
void
testMethod
()
{
FileWriter
fileWriter
=
null
;
try
{
fileWriter
=
new
FileWriter
(
"\data.txt"
);
fileWriter
.
write
(
"Information..."
);
}
catch
(
IOException
ex
)
{
ex
.
printStackTrace
();
}
finally
{
try
{
fileWriter
.
close
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
}
This block is optional and is only used where needed. When used, it is executed last in a try-catch-finally
block and will always be executed, whether or not the try
block terminates normally or the catch
clause(s) were executed. If the finally
block throws an exception, it must be handled.
The try
-with-resources statement is used for declaring resources that must be closed when they are no longer needed. These resources are declared in the try
block:
public
void
testMethod
()
throws
IOException
{
try
(
FileWriter
fw
=
new
FileWriter
(
"\data.txt"
))
{
fw
.
write
(
"Information..."
);
}
}
Any resource that implements the AutoClosable
interface may be used with the try
-with-resources statement.
The multi-catch
clause is used to allow for multiple exception arguments in one catch
clause:
boolean
isTest
=
false
;
public
void
testMethod
()
{
try
{
if
(
isTest
)
{
throw
new
IOException
();
}
else
{
throw
new
SQLException
();
}
}
catch
(
IOException
|
SQLException
e
)
{
e
.
printStackTrace
();
}
}
Here are the steps to the exception handling process:
Programmer-defined exceptions should be created when those other than the existing Java exceptions are necessary. In general, the Java exceptions should be reused wherever possible:
Exception
class, directly or indirectly.
RuntimeException
class, directly or indirectly.
Error
class.
User-defined exceptions should have at least two constructors—a constructor that does not accept any arguments and a constructor that does:
public
class
ReportException
extends
Exception
{
public
ReportException
()
{}
public
ReportException
(
String
message
,
int
reportId
)
{
...
}
}
The methods in the Throwable
class that provide information about thrown exceptions are getMessage()
, toString
, and printStackTrace()
. In general, one of these methods should be called in the catch
clause handling the exception. Programmers can also write code to obtain additional useful information when an exception occurs (i.e., the name of the file that was not found).
The getMessage()
method returns a detailed message string about the exception:
try
{
new
FileReader
(
"file.js"
);
}
catch
(
FileNotFoundException
fnfe
)
{
System
.
err
.
println
(
fnfe
.
getMessage
());
}
This toString()
method returns a detailed message string about the exception, including its class name:
try
{
new
FileReader
(
"file.js"
);
}
catch
(
FileNotFoundException
fnfe
)
{
System
.
err
.
println
(
fnfe
.
toString
());
}
This printStackTrace()
method returns a detailed message string about the exception, including its class name and a stack trace from where the error was caught, all the way back to where it was thrown:
try
{
new
FileReader
(
"file.js"
);
}
catch
(
FileNotFoundException
fnfe
)
{
fnfe
.
printStackTrace
();
}
The following is an example of a stack trace. The first line contains the contents returned when the toString()
method is invoked on an exception object. The remainder shows the method calls beginning with the location where the exception was thrown all the way back to where it was caught and handled:
java
.
io
.
FileNotFoundException
:
file
.
js
(
The
system
cannot
find
the
file
specified
)
at
java
.
io
.
FileInputStream
.
open
(
Native
Method
)
at
java
.
io
.
FileInputStream
.(
init
)
(
FileInputSteam
.
java
:
106
)
at
java
.
io
.
FileInputStream
.(
init
)
(
FileInputSteam
.
java
:
66
)
at
java
.
io
.
FileReader
(
init
)(
FileReader
.
java
:
41
)
at
EHExample
.
openFile
(
EHExample
.
java
:
24
)
at
EHExample
.
main
(
EHExample
.
java
:
15
)