The try/catch guarding statements wrap a block of code and catch designated types of exceptions that occur within it:
try {
readFromFile("foo");
...
}
catch ( Exception e ) {
// Handle error
System.out.println( "Exception while reading file: " + e );
...
}
In this example, exceptions that occur within the body of the try portion of the statement
are directed to the catch clause for possible handling. The catch clause acts like
a method; it specifies as an argument the type of exception it wants to handle and if it’s
invoked, it receives the Exception object as an argument. Here, we receive the object
in the variable e and print it along with a message.
A try statement can have multiple catch clauses that specify different types (subclasses)
of Exception:
try {
readFromFile("foo");
...
}
catch ( FileNotFoundException e ) {
// Handle file not found
...
}
catch ( IOException e ) {
// Handle read error
...
}
catch ( Exception e ) {
// Handle all other errors
...
}
The catch clauses are evaluated in order, and the first assignable match is taken. At
most, one catch clause is executed, which means that the exceptions should be listed
from most to least specific. In the previous example, we anticipate that the hypothetical
readFromFile() can throw two different kinds of exceptions: one for a file not found
and another for a more general read error. In the preceding example, FileNotFoundEx
ception is a subclass of IOException, so if the first catch clause were not there, the
exception would be caught by the second in this case. Similarly, any subclass of Exception is assignable to the parent type Exception, so the third catch clause would catch
anything passed by the first two. It acts here like the default clause in a switch statement
and handles any remaining possibilities. We’ve shown it here for completeness, but in
general you want to be as specific as possible in the exception types you catch.
One beauty of the try/catch scheme is that any statement in the try block can assume
that all previous statements in the block succeeded. A problem won’t arise suddenly because a programmer forgot to check the return value from a method. If an earlier
statement fails, execution jumps immediately to the catch clause; later statements are
never executed.
In Java 7, there is an alternative to using multiple catch clauses, and that is to handle
multiple discrete exception types in a single catch clause using the “|” or syntax:
try {
// read from network...
// write to file..
catch ( ZipException | SSLException e ) {
logException( e );
}
Using this “|” or syntax, we receive both types of exception in the same catch clause.
So, what is the actual type of the e variable that we are passing to our log method? (What
can we do with it?) In this case, it will be neither ZipException nor SSLException but
IOException, which is the two exceptions’ nearest common ancestor (the closest parent
class type to which they are both assignable). In many cases, the nearest common type
among the two or more argument exception types may simply be Exception, the parent
of all exception types. The difference between catching these discrete exception types
with a multiple-type catch clause and simply catching the common parent exception
type is that we are limiting our catch to only these specifically enumerated exception
types and we will not catch all the other IOException types, as would be the alternative
in this case. The combination of multiple-type catch and ordering your catch clauses
from most specific to most broad (“narrow” to “wide”) types gives you great flexibility
to structure your catch clauses to consolidate handling logic where it is appropriate and
to not repeat code. There are more nuances to this feature, and we will return to it after
we have discussed “throwing” and “rethrowing” exceptions.
0 comments:
Post a Comment