Java try, catch, finally and Multi-catch with Examples

Introduction When an exception is thrown, a program does not have to crash - it can catch the exception and decide what to do. Java's tools for this are the try, catch and finally blocks. This tutorial explains try, catch, finally and multi-catch in Java with examples. We will start with the basic try-catch block, and follow exactly how control flows through it. We will then catch several exception types with multiple catch blocks, and see why their order matters. Next we will use multi-catch to handle several types in one block. We will then make sure clean-up code always runs with finally, including its surprises with return. Lastly, we will look at what to do inside a catch block.

(Note - This tutorial builds on Checked vs Unchecked ExceptionsClick to read tutorial: Checked vs Unchecked Exceptions in Java.) The try-catch block Syntax of try-catch
Syntax of try-catch
try {
  // code that might throw an exception
} catch (ExceptionType variableName) {
  // code that handles the exception
}
Where,
  • the statements in the try block run normally. If one of them throws an exception of ExceptionType (or a subclass of it), the rest of the try block is skipped, and the catch block runs.
  • the exception object is available inside the catch block through variableName - usually called e.
  • after the catch block, the program carries on with the statement after the whole try-catch. If no exception is thrown, the catch block is skipped.
Control flow through try-catch
//TryCatchFlow.java
public class TryCatchFlow {
  static void convert(String text) {
    System.out.println("Converting \"" + text + "\"");
    try {
      int value = Integer.parseInt(text);
      System.out.println("  parsed: " + value);
      System.out.println("  doubled: " + value * 2);
    } catch (NumberFormatException e) {
      System.out.println("  not a number: " + e.getMessage());
    }
    System.out.println("  done with \"" + text + "\"");
  }

  public static void main(String[] args) {
    convert("21");
    convert("twenty-one");
  }
}
 OUTPUT of the above code
Converting "21"
  parsed: 21
  doubled: 42
  done with "21"
Converting "twenty-one"
  not a number: For input string: "twenty-one"
  done with "twenty-one"
For "twenty-one", parseInt() threw, so the two printing lines after it in the try block never ran. The catch block handled the exception, and the method carried on normally - the program did not crash.
Multiple catch blocks - and why order matters A try block can be followed by several catch blocks, one for each kind of exception that needs different handling. Java checks them from top to bottom, and runs only the first one whose type matches - a catch block for a class also catches all of its subclasses.
Several catch blocks for different exceptions
//MultipleCatch.java
public class MultipleCatch {
  static void average(String[] values, String countText) {
    try {
      int count = Integer.parseInt(countText);
      int sum = 0;
      for (int i = 0; i < count; i++) {
        sum += Integer.parseInt(values[i]);
      }
      System.out.println("  average = " + sum / count);
    } catch (NumberFormatException e) {
      System.out.println("  bad number: " + e.getMessage());
    } catch (ArrayIndexOutOfBoundsException e) {
      System.out.println("  not enough values: " + e.getMessage());
    } catch (ArithmeticException e) {
      System.out.println("  cannot average zero values");
    } catch (RuntimeException e) {
      System.out.println("  something else went wrong: " + e);
    }
  }

  public static void main(String[] args) {
    String[] values = {"10", "20", "30"};
    average(values, "3");
    average(values, "x");
    average(values, "5");
    average(values, "0");
    average(null, "2");
  }
}
 OUTPUT of the above code
  average = 20
  bad number: For input string: "x"
  not enough values: Index 3 out of bounds for length 3
  cannot average zero values
  something else went wrong: java.lang.NullPointerException: Cannot load from object array because "values" is null
Explanation of the code
  • Each call failed in a different way, and the matching catch block handled it. Passing null caused a NullPointerException, which no specific block names, so the general RuntimeException block caught it.
  • Specific types must come before general ones. If the RuntimeException block came first, it would catch everything, and the blocks after it could never run - so the compiler rejects that order, as shown below.
A general catch block before a specific one
//WrongOrder.java
public class WrongOrder {
  public static void main(String[] args) {
    try {
      Integer.parseInt("x");
    } catch (RuntimeException e) {
      System.out.println("general");
    } catch (NumberFormatException e) {
      System.out.println("specific");
    }
  }
}
 OUTPUT on compiling the above code
WrongOrder.java:8: error: exception NumberFormatException has already been caught
    } catch (NumberFormatException e) {
      ^
1 error
Multi-catch - one block for several types When several exception types need exactly the same handling, Java 7 and later let one catch block list them, separated by |. This removes duplicate code. (The feature was introduced in Catching Multiple Exception Types in a Single Catch Block in Java 7Click to read tutorial on Java 7 multi-catch.)
Handling several exception types in one catch block
//MultiCatch.java
public class MultiCatch {
  static int parseAt(String[] values, int index) {
    try {
      return Integer.parseInt(values[index]);
    } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
      System.out.println("  using 0 because of " + e.getClass().getSimpleName());
      return 0;
    }
  }

  public static void main(String[] args) {
    String[] values = {"7", "seven"};
    System.out.println("index 0 -> " + parseAt(values, 0));
    System.out.println("index 1 -> " + parseAt(values, 1));
    System.out.println("index 5 -> " + parseAt(values, 5));
  }
}
 OUTPUT of the above code
index 0 -> 7
  using 0 because of NumberFormatException
index 1 -> 0
  using 0 because of ArrayIndexOutOfBoundsException
index 5 -> 0
Two rules apply to multi-catch: the types listed cannot be subclasses of each other (IOException | FileNotFoundException is a compile error, because the first already covers the second), and the exception variable is implicitly final, so it cannot be reassigned.
finally - code that always runs A finally block, placed after the catch blocks, runs whether or not an exception was thrown - and even if the try or catch block returns, or throws an exception that is not caught. It is the place for clean-up: closing files and connections, releasing locks, stopping timers. A try block can also be followed by finally alone, with no catch.
finally runs in every case
//FinallyDemo.java
public class FinallyDemo {
  static String process(String input) {
    System.out.println("Processing \"" + input + "\"");
    try {
      if (input.isEmpty()) {
        return "  empty input - returned early";
      }
      int n = Integer.parseInt(input);
      return "  result " + (100 / n);
    } catch (NumberFormatException e) {
      return "  invalid number";
    } finally {
      System.out.println("  finally: releasing resources");
    }
  }

  public static void main(String[] args) {
    System.out.println(process("4"));
    System.out.println(process(""));
    System.out.println(process("abc"));
    try {
      System.out.println(process("0"));
    } catch (ArithmeticException e) {
      System.out.println("  caught in main: " + e.getMessage());
    }
  }
}
 OUTPUT of the above code
Processing "4"
  finally: releasing resources
  result 25
Processing ""
  finally: releasing resources
  empty input - returned early
Processing "abc"
  finally: releasing resources
  invalid number
Processing "0"
  finally: releasing resources
  caught in main: / by zero
Explanation of the code
  • The finally block ran in all four cases - a normal return, an early return, a caught exception, and an ArithmeticException (division by zero) which this method did not catch.
  • In each case, finally ran before the method actually returned or the exception left the method - which is why its message is printed before the result.
  • The only ways to skip finally are extreme ones, such as System.exit() or the JVM crashing.
Note - For resources such as files, streams and connections, try-with-resourcesClick to read tutorial: Java try-with-resources and AutoCloseable Explained closes them automatically and is better than writing a finally block by hand. The finally and return trap Never return (or throw) from a finally block. A return in finally replaces the value returned by the try block - and silently swallows any exception that was being thrown.
return in finally overrides results and hides exceptions
//FinallyReturnTrap.java
public class FinallyReturnTrap {
  @SuppressWarnings("finally")
  static int overridesResult() {
    try {
      return 1;
    } finally {
      return 2;
    }
  }

  @SuppressWarnings("finally")
  static int hidesException() {
    try {
      throw new IllegalStateException("important failure");
    } finally {
      return -1;
    }
  }

  public static void main(String[] args) {
    System.out.println("overridesResult() = " + overridesResult());
    System.out.println("hidesException()  = " + hidesException() + " - the exception disappeared!");
  }
}
 OUTPUT of the above code
overridesResult() = 2
hidesException()  = -1 - the exception disappeared!
The IllegalStateException vanished without a trace - the hardest kind of bug to find. (The compiler warns about this; @SuppressWarnings silences the warning only for this demonstration.) What to do in a catch block Summary In this tutorial we first used try-catch, and followed how control skips the rest of the try block when an exception is thrown, then carries on after the catch. We then used several catch blocks, and saw that specific types must come before general ones. Next we combined types with multi-catch. We then used finally for code that always runs, and saw why it must never contain a return. Lastly, we went through what to do inside a catch block. In the next tutorial, we will throw exceptions ourselves, and see the difference between throw and throwsClick to read tutorial: throw vs throws in Java with Examples.