Exceptions and Exception Handling in Java

The Exception Handling in Java is one of the powerful mechanism to handle the runtime errors so that normal flow of the application can be maintained. Objects of type Throwable and its subtypes can be sent up the stack with the throw keyword and caught with try…catch statements.

Catching an exception with try-catch

An exception can be caught and handled using the try…catch statement. (In fact try statements take other forms, as described in other examples about try…catch…finally and try-with-resources.)

Try-catch with one catch block

The most simple form looks like this:
try {
    doSomething();
} catch (SomeException e) {
    handle(e);
}
// next statement

The behavior of a simple try…catch is as follows:

  • The statements in the try block are executed.
  • If no exception is thrown by the statements in the try block, then control passes to the next statement after the try…catch.
  • If an exception is thrown within the try block.
    • The exception object is tested to see if it is an instance of SomeException or a subtype.
    • If it is, then the catch block will catch the exception:
      • The variable e is bound to the exception object.
      • The code within the catch block is executed.
      • If that code throws an exception, then the newly thrown exception is propagated in place of the original one.
      • Otherwise, control passes to the next statement after the try…catch.
    • If it is not, the original exception continues to propagate.

Try-catch with multiple catches

A try…catch can also have multiple catch blocks. For example:

try {
    doSomething();
} catch (SomeException e) {
    handleOneWay(e)
} catch (SomeOtherException e) {
    handleAnotherWay(e);
}
// next statement

If there are multiple catch blocks, they are tried one at a time starting with the first one, until a match is found for the exception. The corresponding handler is executed (as above), and then control is passed to the next statement after the try…catch statement. The catch blocks after the one that matches are always skipped, even if the handler code throws an exception.

Related Article: The Java Exception Hierarchy – Unchecked and Checked Exceptions

The “top down” matching strategy has consequences for cases where the exceptions in the catch blocks are not disjoint. For example:

try {
    throw new RuntimeException("test");
} catch (Exception e) {
    System.out.println("Exception");
} catch (RuntimeException e) {
    System.out.println("RuntimeException");
}

This code snippet will output “Exception” rather than “RuntimeException”. Since RuntimeException is a subtype of Exception, the first (more general) catch will be matched. The second (more specific) catch will never be executed.

The lesson to learn from this is that the most specific catch blocks (in terms of the exception types) should appear first, and the most general ones should be last. (Some Java compilers will warn you if a catch can never be
executed, but this is not a compilation error.)

Multi-exception catch blocks
Version ≥ Java SE 7

Starting with Java SE 7, a single catch block can handle a list of unrelated exceptions. The exception type are listed, separated with a vertical bar (|) symbol. For example:

try {
    doSomething();
} catch (SomeException | SomeOtherException e) {
    handleSomeException(e);
}

The behavior of a multi-exception catch is a simple extension for the single-exception case. The catch matches if the thrown exception matches (at least) one of the listed exceptions.

There is some additional subtlety in the specification. The type of e is a synthetic union of the exception types in the list. When the value of e is used, its static type is the least common supertype of the type union. However, if e is rethrown within the catch block, the exception types that are thrown are the types in the union. For example:

public void method() throws IOException, SQLException
     try {
         doSomething();
     } catch (IOException | SQLException e) {
         report(e);
         throw e;
     }

In the above, IOException and SQLException are checked exceptions whose least common supertype is Exception. This means that the report method must match report(Exception). However, the compiler knows that the throw can throw only an IOException or an SQLException. Thus, method can be declared as throws IOException, SQLException rather than throws Exception. (Which is a good thing: see Pitfall – Throwing Throwable, Exception, Error or RuntimeException.)

The try-with-resources statement

Version ≥ Java SE 7

As the try-catch-final statement example illustrates, resource cleanup using a finally clause requires a significant amount of “boiler-plate” code to implement the edge-cases correctly. Java 7 provides a much simpler way to deal with this problem in the form of the try-with-resources statement.

What is a resource?

Java 7 introduced the java.lang.AutoCloseable interface to allow classes to be managed using the try-withresources statement. Instances of classes that implement AutoCloseable are referred to as resources. These typically need to be disposed of in a timely fashion rather than relying on the garbage collector to dispose of them.

The AutoCloseable interface defines a single method:

public void close() throws Exception

A close() method should dispose of the resource in an appropriate fashion. The specification states that it should be safe to call the method on a resource that has already been disposed of. In addition, classes that implement Autocloseable are strongly encouraged to declare the close() method to throw a more specific exception than Exception, or no exception at all.

A wide range of standard Java classes and interfaces implement AutoCloseable. These include:

  • InputStream, OutputStream and their subclasses
  • Reader, Writer and their subclasses
  • Socket and ServerSocket and their subclasses
  • Channel and its subclasses, and
  • the JDBC interfaces Connection, Statement and ResultSet and their subclasses.

Application and third party classes may do this as well.

The basic try-with-resource statement

The syntax of a try-with-resources is based on classical try-catch, try-finally and try-catch-finally forms. Here is an example of a “basic” form; i.e. the form without a catch or finally.

try (PrintStream stream = new PrintStream("hello.txt")) {
     stream.println("Hello world!");
}

The resources to be manage are declared as variables in the (…) section after the try clause. In the example above, we declare a resource variable stream and initialize it to a newly created PrintStream.

Once the resource variables have been initialized, the try block is executed. When that completes, stream.close() will be called automatically to ensure that the resource does not leak. Note that the close() call happens no matter
how the block completes.

The enhanced try-with-resource statements

The try-with-resources statement can be enhanced with catch and finally blocks, as with the pre-Java 7 try-catchfinally syntax. The following code snippet adds a catch block to our previous one to deal with the
FileNotFoundException that the PrintStream constructor can throw:

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
} catch (FileNotFoundException ex) {
    System.err.println("Cannot open the file");
} finally {
    System.err.println("All done");
}

If either the resource initialization or the try block throws the exception, then the catch block will be executed. The finally block will always be executed, as with a conventional try-catch-finally statement.

There are a couple of things to note though:

  • The resource variable is out of scope in the catch and finally blocks.
  • The resource cleanup will happen before the statement tries to match the catch block.
  • If the automatic resource cleanup threw an exception, then that could be caught in one of the catch blocks.

Managing multiple resources

The code snippets above show a single resource being managed. In fact, try-with-resources can manage multiple resources in one statement. For example:

try (InputStream is = new FileInputStream(file1);
     OutputStream os = new FileOutputStream(file2)) {
     // Copy 'is' to 'os'
}

This behaves as you would expect. Both is and os are closed automatically at the end of the try block. There are a couple of points to note:

  • The initializations occur in the code order, and later resource variable initializers can use of the values of the earlier ones.
  • All resource variables that were successfully initialized will be cleaned up.
  • Resource variables are cleaned up in reverse order of their declarations.

Thus, in the above example, is is initialized before os and cleaned up after it, and is will be cleaned up if there is an exception while initializing os.

Equivalence of try-with-resource and classical try-catch-finally

The Java Language Specification specifies the behavior of try-with-resource forms in terms of the classical try-catchfinally statement. (Please refer to the JLS for the full details.)

For example, this basic try-with-resource :

try (PrintStream stream = new PrintStream("hello.txt")) {
    stream.println("Hello world!");
}

is defined to be equivalent to this try-catch-finally:

// Note that the constructor is not part of the try-catch statement
PrintStream stream = new PrintStream("hello.txt");

// This variable is used to keep track of the primary exception thrown
// in the try statement. If an exception is thrown in the try block,
// any exception thrown by AutoCloseable.close() will be suppressed.
Throwable primaryException = null;

// The actual try block
try {
    stream.println("Hello world!");
} catch (Throwable t) {
    // If an exception is thrown, remember it for the finally block
    primaryException = t;
    throw t;
} finally {
    if (primaryException == null) {
       // If no exception was thrown so far, exceptions thrown in close() will
       // not be caught and therefore be passed on to the enclosing code.
       stream.close();
    } else {
       // If an exception has already been thrown, any exception thrown in
       // close() will be suppressed as it is likely to be related to the
       // previous exception. The suppressed exception can be retrieved
       // using primaryException.getSuppressed().
       try {
           stream.close();
       } catch (Throwable suppressedException) {
           primaryException.addSuppressed(suppressedException);
       }
    }
}

(The JLS specifies that the actual t and primaryException variables will be invisible to normal Java code.)

The enhanced form of try-with-resources is specified as an equivalence with the basic form. For example:

try (PrintStream stream = new PrintStream(fileName)) {
    stream.println("Hello world!");
} catch (NullPointerException ex) {
    System.err.println("Null filename");
} finally {
    System.err.println("All done");
}

is equivalent to:

try {
     try (PrintStream stream = new PrintStream(fileName)) {
         stream.println("Hello world!");
     }
} catch (NullPointerException ex) {
     System.err.println("Null filename");
} finally {
     System.err.println("All done");
}

Custom Exceptions

Under most circumstances, it is simpler from a code-design standpoint to use existing generic Exception classes when throwing exceptions. This is especially true if you only need the exception to carry a simple error message. In that case, RuntimeException is usually preferred, since it is not a checked Exception. Other exception classes exist for common classes of errors:

  • UnsupportedOperationException – a certain operation is not supported
  • IllegalArgumentException – an invalid parameter value was passed to a method
  • IllegalStateException – your API has internally reached a condition that should never happen, or which occurs as a result of using your API in an invalid way

Cases where you do want to use a custom exception class include the following:

  • You are writing an API or library for use by others, and you want to allow users of your API to be able to specifically catch and handle exceptions from your API, and be able to differentiate those exceptions from other, more generic exceptions.
  • You are throwing exceptions for a specific kind of error in one part of your program, which you want to catch and handle in another part of your program, and you want to be able to differentiate these errors from other, more generic errors.

You can create your own custom exceptions by extending RuntimeException for an unchecked exception, or checked exception by extending any Exception which is not also subclass of RuntimeException, because:

Subclasses of Exception that are not also subclasses of RuntimeException are checked exceptions

public class StringTooLongException extends RuntimeException {
    // Exceptions can have methods and fields like other classes
    // those can be useful to communicate information to pieces of code catching
    // such an exception
    public final String value;
    public final int maximumLength;
     
    public StringTooLongException(String value, int maximumLength){
         super(String.format("String exceeds maximum Length of %s: %s", maximumLength, value));
         this.value = value;
         this.maximumLength = maximumLength;
    }
}

Those can be used just as predefined exceptions:

void validateString(String value){
     if (value.length() > 30){
          throw new StringTooLongException(value, 30);
     }
}

And the fields can be used where the exception is caught and handled:

void anotherMethod(String value){
     try {
         validateString(value);
     } catch(StringTooLongException e){
         System.out.println("The string '" + e.value +
               "' was longer than the max of " + e.maximumLength      );
     }
}

Keep in mind that, according to Oracle’s Java Documentation:

[…] If a client can reasonably be expected to recover from an exception, make it a checked exception. If a client cannot do anything to recover from the exception, make it an unchecked exception.

Handling InterruptedException

InterruptedException is a confusing beast – it shows up in seemingly innocuous methods like Thread.sleep(), but handling it incorrectly leads to hard-to-manage code that behaves poorly in concurrent environments.

At its most basic, if an InterruptedException is caught it means someone, somewhere, called Thread.interrupt() on the thread your code is currently running in. You might be inclined to say “It’s my code! I’ll never interrupt it!” and therefore do something like this:

// Bad. Don't do this.
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // disregard
}

But this is exactly the wrong way to handle an “impossible” event occurring. If you know your application will never encounter an InterruptedException you should treat such an event as a serious violation of your program’s
assumptions and exit as quickly as possible.

The proper way to handle an “impossible” interrupt is like so:

// When nothing will interrupt your code
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
     Thread.currentThread().interrupt();
     throw new AssertionError(e);
}

This does two things; it first restores the interrupt status of the thread (as if the InterruptedException had not been thrown in the first place), and then it throws an AssertionError indicating the basic invariants of your
application have been violated. If you know for certain that you’ll never interrupt the thread this code runs in this is safe since the catch block should never be reached.

Using Guava’s Uninterruptibles class helps simplify this pattern; calling
Uninterruptibles.sleepUninterruptibly() disregards the interrupted state of a thread until the sleep duration has expired (at which point it’s restored for later calls to inspect and throw their own InterruptedException). If you know you’ll never interrupt such code this safely avoids needing to wrap your sleep calls in a try-catch block.

More often, however, you cannot guarantee that your thread will never be interrupted. In particular if you’re writing code that will be executed by an Executor or some other thread-management it’s critical that your code responds promptly to interrupts, otherwise your application will stall or even deadlock.

In such cases the best thing to do is generally to allow the InterruptedException to propagate up the call stack, adding a throws InterruptedException to each method in turn. This may seem kludgy but it’s actually a desirable property – your method’s signatures now indicates to callers that it will respond promptly to interrupts.

// Let the caller determine how to handle the interrupt if you're unsure
public void myLongRunningMethod() throws InterruptedException {

}

In limited cases (e.g. while overriding a method that doesn’t throw any checked exceptions) you can reset the interrupted status without raising an exception, expecting whatever code is executed next to handle the interrupt. This delays handling the interruption but doesn’t suppress it entirely.

// Suppresses the exception but resets the interrupted state letting later code
// detect the interrupt and handle it properly.
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    return …; // your expectations are still broken at this point - try not to do more work.
}

Return statements in try catch block

Although it’s bad practice, it’s possible to add multiple return statements in a exception handling block:

public static int returnTest(int number){
     try{
        if(number%2 == 0) throw new Exception("Exception thrown");
        else return x;
     }
     catch(Exception e){
        return 3;
     }
     finally{
        return 7;
     }
}

This method will always return 7 since the finally block associated with the try/catch block is executed before anything is returned. Now, as finally has return 7;, this value supersedes the try/catch return values.

If the catch block returns a primitive value and that primitive value is subsequently changed in the finally block, the value returned in the catch block will be returned and the changes from the finally block will be ignored.

The example below will print “0”, not “1”.

public class FinallyExample {

     public static void main(String[] args) {
         int n = returnTest(4);

         System.out.println(n);
     }

     public static int returnTest(int number) {

         int returnNumber = 0;

         try {
             if (number % 2 == 0)
                throw new Exception("Exception thrown");
             else
                return returnNumber;
         } catch (Exception e) {
             return returnNumber;
         } finally {
             returnNumber = 1;
         }
    }
}

Introduction

Exceptions are errors which occur when a program is executing. Consider the Java program below which divides two integers.

class Division {
     public static void main(String[] args) {

        int a, b, result;

        Scanner input = new Scanner(System.in);
        System.out.println("Input two integers");

        a = input.nextInt();
        b = input.nextInt();

        result = a / b;

        System.out.println("Result = " + result);
   }
}

Now we compile and execute the above code, and see the output for an attempted division by zero:

Input two integers
7 0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Division.main(Disivion.java:14)

Division by zero is an invalid operation that would produce a value that cannot be represented as an integer. Java deals with this by throwing an exception. In this case, the exception is an instance of the ArithmeticException class.

Note: The example on creating and reading stack traces explains what the output after the two numbers means.

The utility of an exception is the flow control that it allows. Without using exceptions, a typical solution to this problem may be to first check if b == 0:

class Division {
     public static void main(String[] args) {
         int a, b, result;

         Scanner input = new Scanner(System.in);
         System.out.println("Input two integers");

         a = input.nextInt();
         b = input.nextInt();

         if (b == 0) {
             System.out.println("You cannot divide by zero.");
             return;
         }
         result = a / b;

         System.out.println("Result = " + result);
    }
}

This prints the message You cannot divide by zero. to the console and quits the program in a graceful way when the user tries to divide by zero. An equivalent way of dealing with this problem via exception handling would be to replace the if flow control with a try-catch block:

…
a = input.nextInt();
b = input.nextInt();

try {
    result = a / b;
}
catch (ArithmeticException e) {
    System.out.println("An ArithmeticException occurred. Perhaps you tried to divide by zero.");
    return;
}
…

A try catch block is executed as follows:

  1. Begin executing the code in the try block.
  2. If an exception occurs in the try block, immediately abort and check to see if this exception is caught by the catch block (in this case, when the Exception is an instance of ArithmeticException).
  3. If the exception is caught, it is assigned to the variable e and the catch block is executed.
  4. If either the try or catch block is completed (i.e. no uncaught exceptions occur during code execution) then continue to execute code below the try-catch block.

It is generally considered good practice to use exception handling as part of the normal flow control of an application where behavior would otherwise be undefined or unexpected. For instance, instead of returning null when a
method fails, it is usually better practice to throw an exception so that the application making use of the method can define its own flow control for the situation via exception handling of the kind illustrated above. In some sense, this gets around the problem of having to return a particular type, as any one of multiple kinds of exceptions may be thrown to indicate the specific problem that occurred.

LEAVE A REPLY

Please enter your comment!
Please enter your name here