Inner Classes and Exceptions ·
‹ Previous Next ›
⏱ 5 min read Modified: 2026-09-25

Java Try Catch Finally: Exception Handling with Examples

What does try { return 1; } finally { return 2; } return? The answer is 2 — and if the try block had thrown an exception instead, a return in finally would silently swallow it. Details like this, together with the order of catch blocks, are among the most common Java interview questions about exception handling.

Try-catch-finally in Java is the core exception handling construct: the try block contains code that may throw an exception, one or more catch blocks catch and handle exceptions of the specified types, and the optional finally block runs after try and catch almost always — typically to release resources. Both checked and unchecked exceptions can be caught.

Try Catch Finally Syntax in Java

The general form of a try-catch-finally statement:

try {
    // code that may throw an exception
} catch (ExceptionType1 e) {
    // handler for ExceptionType1
} catch (ExceptionType2 e) {
    // handler for ExceptionType2
} finally {
    // code that runs after try and catch
}

The catch and finally blocks are each optional, but a try block can never stand alone. Valid combinations are:

  • try-catch — catch and handle an exception;
  • try-finally — no handling, but the cleanup code is guaranteed to run;
  • try-catch-finally — handling plus cleanup code;
  • try-with-resources (Java 7+) — may have neither catch nor finally; resources are closed automatically.

A bare try { ... } without catch, finally or resources is a compile-time error.

Java Try Catch: Rules and Examples

A try block is the place where a checked or unchecked exception can be thrown. A catch block handles the exception: its parameter declares the exception type and gives you a reference to the exception object.

Rules for writing try-catch in Java:

  • Only one try block is allowed per statement.
  • You can write any number of catch blocks.
  • The catch blocks must immediately follow the try block.
  • The catch blocks must follow each other, with no other statements or blocks in between.
  • A try block without either a catch block or a finally block is illegal (try-with-resources is the only exception).
  • The finally block must immediately follow the last catch block, or the try block if there is no catch.

Example 1. Catching an Unchecked Exception

The call str.substring(10, 20) throws StringIndexOutOfBoundsException because the string is only 19 characters long. Control jumps straight to the matching catch block, so the statement that prints "The end of try block" is never executed.

try {
    String str = "Hello Java world!!!";
    System.out.println(str.substring(0, 5));
    System.out.println(str.substring(10, 20)); // throws StringIndexOutOfBoundsException
    System.out.println("The end of try block");  // skipped
} catch (StringIndexOutOfBoundsException e) {
    System.out.println("In the catch block");
}
System.out.println("Done");

The output is:

Hello
In the catch block
Done

Example 2. Multiple Catch Blocks, No Exception Thrown

Now the indexes are valid, so no exception is thrown: every statement in the try block runs, all catch blocks are skipped, and execution continues after the whole try-catch statement. A second catch block is added to show that several handlers can follow one try:

try {
    String str = "Hello Java world!!!";
    System.out.println(str.substring(0, 5));
    System.out.println(str.substring(11, 16));
    System.out.println("The end of try block");
} catch (StringIndexOutOfBoundsException e) {
    System.out.println("In the StringIndexOutOfBoundsException catch block");
} catch (ArrayIndexOutOfBoundsException e) {
    System.out.println("In the ArrayIndexOutOfBoundsException catch block");
}
System.out.println("Done");

The output is:

Hello
world
The end of try block
Done

When an exception is thrown, the catch blocks are checked from top to bottom, and only the first one whose type matches the exception (or is its superclass) runs. The remaining catch blocks are skipped.

The Finally Block

The finally block is optional. It contains code that runs after the try block whether an exception was thrown or not, which makes it the traditional place for cleanup: closing files, releasing locks, resetting state.

  • If the try block completes without exceptions, finally runs immediately after it.
  • If an exception is thrown and caught, finally runs immediately after the matching catch block completes.
  • If an exception is thrown and no catch block matches it, finally still runs, and then the exception propagates to the caller.
  • If the try or catch block contains return, the return value is evaluated first, then finally runs, and only then does the method actually return.
  • If an exception is thrown (or re-thrown) inside a catch block, finally runs before that exception leaves the statement.

Example 3. Try-Catch-Finally

Example 1 with a finally block added:

try {
    String str = "Hello Java world!!!";
    System.out.println(str.substring(0, 5));
    System.out.println(str.substring(10, 20));
    System.out.println("The end of try block");
} catch (StringIndexOutOfBoundsException e) {
    System.out.println("In the catch block");
} finally {
    System.out.println("In the finally block");
}
System.out.println("Done");

The output is:

Hello
In the catch block
In the finally block
Done

Try Finally Without Catch

Can you use try without catch in Java? Yes — if a finally block is present, catch is optional. A try-finally statement does not handle the exception: it lets it propagate to the caller, but guarantees that the cleanup code runs first.

try {
    System.out.println("In the try block");
} finally {
    System.out.println("In the finally block");
}
System.out.println("Done");

The output is:

In the try block
In the finally block
Done

The next example shows all three ways to leave a try-finally block: through an exception, through return, and by normal completion. The exception from procA() is not handled locally — it is caught in main, but finally runs before that.

public class FinallyUse {
    // leaving the method with an exception
    static void procA() {
        try {
            System.out.println("Inside procA");
            throw new RuntimeException("demo");
        } finally {
            System.out.println("procA's finally");
        }
    }

    // returning from inside the try block
    static void procB() {
        try {
            System.out.println("Inside procB");
            return;
        } finally {
            System.out.println("procB's finally");
        }
    }

    // normal completion of the try block
    static void procC() {
        try {
            System.out.println("Inside procC");
        } finally {
            System.out.println("procC's finally");
        }
    }

    public static void main(String[] args) {
        try {
            procA();
        } catch (Exception e) {
            System.out.println("Exception caught");
        }
        procB();
        procC();
    }
}

The output is:

Inside procA
procA's finally
Exception caught
Inside procB
procB's finally
Inside procC
procC's finally

When Finally Runs and When It Doesn't

"Finally always runs" is a useful rule of thumb, but not an absolute guarantee. The table lists the typical cases:

Situation Does finally run? Comment
try completes without exceptions Yes Right after try
Exception caught by a catch block Yes After the catch block completes
Exception not caught by any catch block Yes Then the exception propagates to the caller
New exception thrown inside catch Yes Then the new exception propagates
return, break or continue in try/catch Yes Before control actually leaves the statement
System.exit() or Runtime.getRuntime().halt() in try/catch No The JVM terminates immediately
JVM crash or forced process kill No For example, kill -9 or a power failure
Infinite loop or deadlock in try No Control never reaches finally
Daemon thread while the JVM shuts down Not guaranteed Daemon threads are stopped without running finally

Try Catch Finally and Return: Tricky Cases

If both try and finally contain return, the method returns the value from finally — it overrides the result of the try block:

public class FinallyReturn {
    static int getValue() {
        try {
            return 1;
        } finally {
            return 2; // overrides return from try
        }
    }

    static int lostException() {
        try {
            throw new RuntimeException("Error");
        } finally {
            return 0; // the exception is silently discarded
        }
    }

    public static void main(String[] args) {
        System.out.println(getValue());      // 2
        System.out.println(lostException()); // 0, no exception
    }
}

In the second method the exception simply disappears: return in finally cancels its propagation, and the caller never learns that something went wrong.

Important

Avoid return, break, continue and throw inside finally: they replace the method's result and can hide an exception. The compiler allows it (javac only warns with -Xlint:finally), but IDEs and static analyzers flag it as a design error.

Catching Subclasses: Order of Catch Blocks

A catch block catches not only exceptions of the specified class, but also all of its subclasses. For example, IndexOutOfBoundsException has two subclasses, ArrayIndexOutOfBoundsException and StringIndexOutOfBoundsException, so Example 2 can be rewritten with a single catch block:

try {
    String str = "Hello Java world!!!";
    System.out.println(str.substring(0, 5));
    System.out.println(str.substring(11, 16));
    System.out.println("The end of try block");
} catch (IndexOutOfBoundsException e) {
    // handles both ArrayIndexOutOfBoundsException and StringIndexOutOfBoundsException
    System.out.println("In the IndexOutOfBoundsException catch block");
}
System.out.println("Done");

If an exception matches several catch blocks, only the first one runs. That is why a catch block for a subclass must come before a catch block for its superclass. In the next example new FileReader(...) may throw FileNotFoundException, a subclass of IOException, which in turn is a subclass of Exception. If the file is missing, only the first catch block runs:

static void openFile(String fileName) {
    try {
        FileReader reader = new FileReader(fileName);
        reader.close();
    } catch (IOException e) {
        System.out.println("In the IOException catch block");
    } catch (Exception e) {
        System.out.println("In the Exception catch block");
    }
}

Swap the two blocks, and the code will not compile: the catch (IOException e) block becomes unreachable, and the compiler reports exception java.io.IOException has already been caught.

One more compiler rule is easy to get wrong. A catch block for an unchecked exception is allowed even if the try block can never throw it (Example 2 does exactly that with ArrayIndexOutOfBoundsException). But a catch block for a checked exception that the try block cannot throw is a compile-time error. The rule does not apply to Exception and Throwable, because they also cover unchecked exceptions.

This code does not compile:

try {
    System.out.println("In the try block");
} catch (IOException e) { // error: exception java.io.IOException is never thrown
                          // in body of corresponding try statement
    System.out.println("In the IOException catch block");
}

Try-with-Resources

Since Java 7, the try-with-resources statement lets you declare one or more resources in parentheses after try. They are closed automatically at the end of the block, so you do not need an explicit finally block for cleanup.

  • Any object that implements java.lang.AutoCloseable (including every java.io.Closeable) can be used as a resource.
  • Resources are closed in the reverse order of their declaration.
  • Resources declared in the parentheses are out of scope after the block and already closed.
  • If both the try block and close() throw exceptions, the exception from the try block "wins"; the exceptions from close() are attached to it as suppressed and are available via getSuppressed().
  • Since Java 9, an existing final or effectively final variable can be used as a resource: try (reader) { ... }.

The BufferedReader below is closed whether the try block completes normally or not:

public String readFirstLine(String fileName) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
        return br.readLine();
    }
}

Several resources are separated by semicolons:

try (FileReader fr = new FileReader("a.txt");
     BufferedReader br = new BufferedReader(fr)) {
    System.out.println(br.readLine());
} // br is closed first, then fr

A try-with-resources statement can have its own catch and finally blocks; they run after the resources have been closed.

Multi-Catch: Multiple Exceptions in One Catch Block

Since Java 7, a single catch block can handle several exception types separated by a vertical bar |. This is called multi-catch. It removes duplicated handlers and reduces the temptation to catch an overly broad exception such as Exception.

Before Java 7, identical handlers had to be repeated:

try {
    // ...
} catch (IllegalStateException e) {
    System.out.println(e.getMessage());
} catch (IOException e) {
    System.out.println(e.getMessage());
} catch (SQLException e) {
    System.out.println(e.getMessage());
}

With multi-catch the same logic is shorter:

try {
    // ...
} catch (IllegalStateException | IOException | SQLException e) {
    System.out.println(e.getMessage());
}

The alternatives in a multi-catch block must not be subclasses of each other. For example, catch (FileNotFoundException | IOException e) does not compile, because FileNotFoundException extends IOException — listing IOException alone is enough. Unrelated types, like IOException and SQLException, can be combined freely.

Note

The parameter of a multi-catch block is implicitly final: assigning a new value to it (e = new IOException();) is a compile-time error. In a regular single-type catch block the parameter is not final and can be reassigned, unless you declare it final explicitly.

Frequently Asked Questions

Is there a try-catch-else in Java, like in Python?

No, Java has no try-catch-else construct. Code that should run only when no exception occurs is simply placed at the end of the try block, after the risky operations. Code that must run in every case goes into finally.

Does changing a variable in finally change the returned value?

Not for primitives and immutable objects. In int x = 1; try { return x; } finally { x = 10; } the method returns 1, because the return value is evaluated and stored before finally runs. But if you return a reference to a mutable object, such as a list, and modify its contents in finally, the caller will see the changes.

What happens if an exception is thrown inside catch or finally?

An exception thrown in a catch block is not caught by the other catch blocks of the same statement — they only cover the try block. Finally runs, and then the new exception propagates to the caller. An exception thrown in finally replaces any exception that was already propagating, so the original one is lost. To keep the original cause, pass it to the constructor: throw new MyException("...", e).

What is the difference between multi-catch and catch (Exception e)?

Multi-catch catches only the listed types, for example catch (IOException | SQLException e), and lets every other exception propagate. catch (Exception e) catches all checked and unchecked exceptions, including programming errors like NullPointerException that you usually should not hide.

Should I close resources in finally or use try-with-resources?

Use try-with-resources. It is shorter, closes resources automatically in reverse order and keeps exceptions from close() as suppressed instead of losing them. A manual finally block is still the right choice for cleanup that is not an AutoCloseable, such as lock.unlock().

Comments

Please log in or register to have a possibility to add comment.