Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

try, catch and finally

An exception doesn't have to crash the program. You can catch it and decide what to do next: show a clear message, use a default value, try again. The try-catch statement is made for this.

try and catch

Code that may throw an exception goes into a try block. It is followed by a catch block with the type of exception to catch:

void main() {
    try {
        int result = 10 / 0;
        IO.println(result);
    } catch (ArithmeticException e) {
        IO.println("You can't divide by zero");
    }
    IO.println("The program keeps running");
}

Output:

You can't divide by zero
The program keeps running

When an exception occurs in try, the rest of the block is skipped and control jumps to the matching catch. After it the program continues as usual. If there was no exception, catch doesn't run at all.

The exception object

The parentheses of catch declare a variable, usually called e. It gives access to the error message:

void main() {
    String input = "12a";
    try {
        int number = Integer.parseInt(input);
        IO.println(number * 2);
    } catch (NumberFormatException e) {
        IO.println("Error: " + e.getMessage());
    }
}

Output:

Error: For input string: "12a"

Several catch blocks

One try can have several catch blocks — a different reaction for each error. Java checks them from top to bottom and runs the first one that matches. If the reaction is the same, you can list the types with | in a single block: catch (NumberFormatException | ArithmeticException e) — this is called multi-catch.

void check(String text, int index) {
    try {
        int value = Integer.parseInt(text);
        int[] data = new int[3];
        data[index] = value;
        IO.println("Stored: " + value);
    } catch (NumberFormatException e) {
        IO.println("Not a number: " + text);
    } catch (ArrayIndexOutOfBoundsException e) {
        IO.println("Bad index: " + index);
    }
}
 
void main() {
    check("7", 1);
    check("seven", 1);
    check("7", 5);
}

Output:

Stored: 7
Not a number: seven
Bad index: 5

finally

The finally block runs always: whether there was an exception or not, whether it was caught or not. It is usually where resources are released — files and connections are closed:

void main() {
    try {
        IO.println("Opening the connection");
        int x = 5 / 0;
    } catch (ArithmeticException e) {
        IO.println("Error: " + e.getMessage());
    } finally {
        IO.println("Closing the connection");
    }
}

Output:

Opening the connection
Error: / by zero
Closing the connection

There is a handier way to close resources — try-with-resources, which has a lesson of its own.

throw

You can throw exceptions yourself with the throw statement. This is how a method reports that it got invalid data:

void setAge(int age) {
    if (age < 0) {
        throw new IllegalArgumentException("Age cannot be negative: " + age);
    }
    IO.println("Age: " + age);
}
 
void main() {
    setAge(30);
    try {
        setAge(-5);
    } catch (IllegalArgumentException e) {
        IO.println(e.getMessage());
    }
}

Output:

Age: 30
Age cannot be negative: -5

Common mistake

catch blocks are checked in order, so a general type can't come before a specific one. Exception also catches ArithmeticException, so the second block could never run — the compiler treats this as an error:

void main() {
    try {
        int x = 10 / 0;
    } catch (Exception e) {
        IO.println("Something went wrong");
    } catch (ArithmeticException e) {
        IO.println("Division by zero");
    }
}

Output:

Main.java:6: error: exception ArithmeticException has already been caught
    } catch (ArithmeticException e) {
      ^
1 error
error: compilation failed

Put specific exceptions above general ones. And don't overuse catch (Exception e): it's better to catch only the errors you really know how to handle.

Thanks for reading!

Donate to the site — we work hard for you.