The first thing to do is try to find documentation on the APIs you are using to read the text and XML files. Javadoc, which is what the Java API is documented with, will automatically document all exceptions a method declares. In most cases, catching those exceptions is enough. Also, make sure all method parameters are correct so that a method does not unexpectedly throw NullPointerException
, IllegalArgumentException
, or something similar.
If you do not know about checked and unchecked exceptions, go read about it now (e.g. Checked vs Unchecked Exception). In short, a method cannot throw a checked exception without declaring it in a throws
clause, and the method caller must either catch and handle the exception or declare that it throws the exception. Only exceptions that extend RuntimeException
may be thrown by a method without a throws
clause, although sometimes programmers declare and/or document these too.
Make sure you understand the different types of exceptions. Anything throwable extends Throwable
(See Throwable Javadoc). Error
and Exception
extend throwable. Error
throwables are generally ones you cannot do much about, such as OutOfMemoryError
, so it is usually safe to ignore these. Exceptions
are the ones you are concerned with. RuntimeException
extends Exception
, and a runtime exception is unchecked. Anything else that extends Exception
is checked.
IO operations generally throw IOException
, FileNotFoundException
, and SecurityException
. IOException
is checked, and since FileNotFoundException
extends IOException
, it is also checked (catching just IOException
will handle both). They can be handled separately, but FileNotFoundException
will need to be caught before IOException
. SecurityException
is unchecked, and you are unlikely to see it unless the code is running in a JVM using the security manager to restrict the program (access denied by file permissions is likely to throw IOException
).
If you are not sure that your code is catching all the exceptions and handling them properly, then you can always catch Exception
and handle that. Alternatively, you can catch Throwable
, but then you risk ignoring errors that your program cannot handle and crashing less gracefully. If you do catch Throwable
, its a good idea to use it for capturing detailed information and crashing gracefully, such as logging or a GUI application trying to display the error before disappearing from the screen.
Lastly, have a plan for handling the exception. An error I have seen too often is catching an exception, printing and error, and then going on to crash because an IO resource was not initialized. If the method cannot fully recover or crash gracefully, let it throw the exception. Don't forget that you can append information to an exception and use exception chaining so that callees can print a more meaningful error.
For example, suppose your program can read some settings file and it processes it line-by-line. The following method assumes the argument, fileName
has been checked already, although you should check arguments explicitly on public
methods and throw NullPointerException
or IllegalArgumentException
accordingly.
/**
* Read a settings file and configure this object.
*
* @param fileName Name of the file. Must not be <code>null</code>
* or empty.
*
* @throws IOException If an IO error occurs while opening or
* reading the file.
* @throws SecurityException If a security manager exists and its
* <code>checkRead()</code> method denies read access to the file.
*/
private void loadSettingsFile(String fileName)
throws IOException, SecurityException {
String line; // Line read from file
// Read file
try (BufferedReader reader = new BufferedReader(new FileReader(new File(fileName)))) {
while ((line = reader.readLine()) != null) {
line = line.trim();
// Do something with line
}
} catch (IOException ex) {
throw new IOException("IO error reading settings file: " + ex.getMessage(), ex);
} catch (SecurityException ex) {
throw new SecurityException("Security error reading settings file: " + ex.getMessage(), ex);
} catch (Exception ex) {
throw new IOException(String.format("Unknown error reading settings file: %s: %s", fileName, ex.getMessage()), ex);
}
return;
}
In this example, SecurityException
and IOException
(which also catches FileNotFoundException
) are caught and a new exception of the same type is generated with additional information, such as what the program was doing when the exception was thrown. Including the original exception in the new exception (second argument to the constructor) preserves the full stack trace (this is exception chaining). I know that when IOException
and SecurityException
is thrown in this code, the file name is included; I don't need to add it to the exception again. When other exceptions are thrown, it may not be included, so I explicitly do that when catching Exception
.
Note that FileReader()
does not declare nor document SecurityException
, but it does throw it (verified in OpenJDK 1.7.0_91). This gets back to your question about how you catch all exceptions. Sometimes they are not documented, and you have to guess based on experience. Catching Exception
from code you don't know or don't trust is also an option.
In this example, I chose to rethrow Exception
as IOException
since my callee is already handling those. As a matter of style, I prefer to document all exceptions (even unchecked ones), so this judgment call made sense to me.