Java Language


Java Pitfalls - Exception usage All Versions

Java SE 1.0
Java SE 1.1
Java SE 1.2
Java SE 1.3
Java SE 1.4
Java SE 5
Java SE 6
Java SE 7
Java SE 8
Java SE 9 (Early Access)

This draft deletes the entire topic.

expand all collapse all

Examples

  • 4

    A common thought pattern for inexperienced Java programmers is that exceptions are "a problem" or "a burden" and the best way to deal with this is catch them all1 as soon as possible. This leads to code like this:

    ....
    try {
        InputStream is = new FileInputStream(fileName);
        // process the input
    } catch (Exception ex) {
        System.out.println("Could not open file " + fileName);
    }
    

    The above code has a significant flaw. The catch is actually going to catch more exceptions than the programmer is expecting. Suppose that the value of the fileName is null, due to a bug elsewhere in the application. This will cause the FileInputStream constructor to throw a NullPointerException. The handler will catch this, and report to the user:

        Could not open file null
    

    which is unhelpful and confusing. Worse still, suppose that the it was the "process the input" code that threw the unexpected exception (checked or unchecked!). Now the user will get the misleading message for a problem that didn't occur while opening the file, and may not be related to I/O at all.

    The root of the problem is that the programmer has coded a handler for Exception. This is almost always a mistake:

    • Catching Exception will catch all checked exceptions, and most unchecked exceptions as well.
    • Catching RuntimeException will catch most unchecked exceptions.
    • Catching Error will catch unchecked exceptions that signal JVM internal errors. These errors are generally not recoverable, and should not be caught.
    • Catching Throwable will catch all possible exceptions.

    The problem with catching too broad a set of exceptions is that the handler typically cannot handle all of them appropriately. In the case of the Exception and so on, it is difficult for the programmer to predict what could be caught; i.e. what to expect.

    There are very few situations where catching Exception is appropriate. The only one that arises commonly is something like this:

    public static void main(String[] args) {
        try {
            // do stuff
        } catch (Exception ex) {
            System.err.println("Unfortunately an error has occurred. " +
                               "Please report this to X Y Z");
            // Write stacktrace to a log file.
            System.exit(1);
        }
    }
    

    Here we genuinely want to deal with all exceptions, so catching Exception (or even Throwable) is correct.


    1 - Also known as Pokemon Exception Handling.

  • 4

    This example is about deliberately ignoring or "squashing" exceptions. Or to be more precise, it is about how to catch and handle an exception in a way that ignores it. However, before we describe how to do this, we should first point out that squashing exceptions is generally not the correct way to deal with them.

    Exceptions are usually thrown (by something) to notify other parts of the program that some significant (i.e. "exceptional") event has occurred. Generally (though not always) and exception means that something has gone wrong. If you code your program to squash the exception, there is a fair chance that the problem will reappear in another form. To make things worse, when you squash the exception, you are throwing away the information in the exception object and its associated stack trace. That is likely to make it harder to figure out what the original source of the problem was.

    In practice, exception squashing frequently happens when you use an IDE's auto-correction feature to "fix" a compilation error caused by an unhandled exception. For example, you might see code like this:

    try {
        inputStream = new FileInputStream("someFile");
    } catch (IOException e) {
        /* add exception handling code here */
    }
    

    Clearly, the programmer has accepted the IDE's suggestion to make the compilation error go away, but the suggestion was inappropriate. (If the file open has failed, the program should most likely do something about it. With the above "correction", the program is liable to fail later; e.g. with a NullPointerException because inputStream is now null.)

    Having said that, here is an example of deliberately squashing an exception. (For the purposes of argument, assume that we have determined that an interrupt while showing the selfie is harmless.) The comment tells the reader that we squashed the exception deliberately, and why we did that.

    try {
        selfie.show();
    } catch (InterruptedException e) {
        // It doesn't matter if showing the selfie is interrupted.
    }
    

    Another conventional way to highlight that we are deliberately squashing an exception without saying why is to indicate this with the exception variable's name, like this:

    try { 
        selfie.show(); 
    } catch (InterruptedException ignored) {  }
    

    Some IDEs (like IntelliJ IDEA) won't display a warning about the empty catch block if the variable name is set to ignored.

  • 2

    While catching the Throwable, Exception, Error and RuntimeException exceptions is bad, throwing them is even worse.

    The basic problem is that when your application needs to handle exceptions, the presence of the top level exceptions make it hard to discriminate between different error conditions. For example

    try {
        InputStream is = new FileInputStream(someFile);  // could throw IOException
        ...
        if (somethingBad) {
            throw new Exception();  // WRONG
        }
    } catch (IOException ex) {
        System.err.println("cannot open ...");
    } catch (Exception ex) {
        System.err.println("something bad happened");  // WRONG
    }
    

    The problem is that because we threw an Exception instance, we are forced to catch it. However as described in another example, catching Exception is bad. In this situation, it becomes difficult to discriminate between the "expected" case of an Exception that gets thrown if somethingBad is true, and the unexpected case where we actually catch an unchecked exception such as NullPointerException.

    If the top-level exception is allowed to propagate, we run into other problems:

    • We now have to remember all of the different reasons that we threw the top-level, and discriminate / handle them.
    • In the case of Exception and Throwable we also need to add these exceptions to the throws clause of methods if we want the exception to propagate. This is problematic, as described below.

    In short, don't throw these exceptions. Throw a more specific exception that more closely describes the "exceptional event" that has happened. If you need to, define and use a custom exception class.

    Declaring Throwable or Exception in a method's "throws" is problematic.

    It is tempting to replace a long list of thrown exceptions in a method's throws clause with Exception or even `Throwable. This is a bad idea:

    1. It forces the caller to handle (or propagate) `Exception`.
    2. We can no longer rely on the compiler to tell us about specific checked exceptions that need to be handled.
    3. Actually handling `Exception` or `Throwable` properly is difficult because it is hard to know what the actual exception is.
    

    This advice means that certain other patterns should be avoided. For example:

    try {
        doSomething();
    } catch (Exception ex) {
        report(ex);
        throw ex;
    }
    

    The above attempts to log all exceptions as they pass, without definitively handling them. Unfortunately, prior to Java 7, the throw ex; statement caused the compiler to think that any Exception could be thrown. That could force you to declare the enclosing method as throws Exception. From Java 7 onwards, the compiler knows that the set of exceptions that could be (re-thrown) there is smaller.

Please consider making a request to improve this example.

Syntax

Syntax

Parameters

Parameters

Remarks

Remarks

Still have a question about Java Pitfalls - Exception usage? Ask Question

Topic Outline