Exceptions: the try/catch block
In our introduction to exceptions,
we showed how an exception object thrown by a method
(in fact a constructor in the example) could be caught by the caller in a
construction commonly called the try/catch block. In the
block preceded by try, we put the code whose exceptions we want or need
to trap. In the block preceded by catch, we put the code that will be
executed if and only if an exception of the given type is thrown. Let's
expand on this construction here. Firstly, we
didn't explicitly state the following:
- The try and catch blocks can contain arbitrary number of lines of code:
in other words, with a single block, we can catch exceptions over a whole section of code.
This makes things much more succinct than the traditional check for an error condition
after every line typical in C programming.
- So long as at least one of the lines in the try block can throw an exception
of the given type, we can have other lines in the same block which don't actually throw the given exception.
- We can have multiple catch blocks associated with a single try block,
to catch different types of exceptions.
As an illustration of these points, consider the following code, which opens a
file, reads a line from it, and parses the line. In this example, the first line of the try
block can throw a FileNotFoundException and some of the later lines can throw
an IOException. Additionally, there is one line that can throw a NumberFormatException.
The call to line.trim() can't actually throw any of these exceptions, but it
doesn't matter: we can still include it in the same try block to avoid having to
make our code messy with multiple try/catch blocks. Notice how we can even put the
return statement to the method inside the try/catch block. (It's important to
state that this is an example of the points mentioned so far: in reality, there
are other changes we'd need to make to this code, as discussed below.)
public int getNumber(File f) {
try {
FileInputStream fin = new FileInputStream(f);
BufferedReader br = new BufferedReader(new InputStreamReader(fin), "ASCII");
String line = br.readLine();
line = line.trim();
int number = Integer.parseInt(line);
br.close();
return number;
} catch (FileNotFoundException fnf) {
// Oh dear, error occurred opening file
displayErrorMessage(fnf.getMessage());
} catch (IOException ioex) {
displayErrorMessage(ioex.getMessage());
} catch (NumberFormatException nfe) {
displayErrorMessage("The file contained invalid data");
}
return -1; // If we get here, we didn't read a
// number from the file
}
(Note: we're not going to get bogged down in the actual calls to perform the I/O; to learn more
about things such as the BufferedReader and the readLine() method,
see this site's separate introduction to I/O in Java.)
Problems with this example (and how to resolve them)
Now, there are actually still some inconveniences with the above example that
we'd need to resolve:
- in the case of every exception, we're actually logging the error there and then; in reality,
we might not want to mix file handling and user interface code in utility methods in this way; it would
better to let the caller handle the error and manage things such as UI messages;
- on a related issue, we're actually still returning a spurious value from our
method when an error occurs: astute readers will recall that one of the whole points of exceptions
was to properly separate return values from error states;
- if an exception occurred after the file was created, we woudldn't close the file.
It turns out that there's also no need for us to handle FileNotFoundException and
IOException separately (unless we really want to). This is because FileNotFoundException
is actually an extension (subclass) of IOException. So just catching or
handling IOException is enough to cover both cases.
On the next pages, we'll look at how to resolve these problems:
- using a throws declaration, we can make our
method throw exceptions 'back up' to the caller for the caller to handle rather than having to handle them there
and then;
- we can use a finally block to make sure the file is closed whether an exception occurs or not.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.