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 neithercatchnorfinally; 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
tryblock is allowed per statement. - You can write any number of
catchblocks. - The
catchblocks must immediately follow thetryblock. - The
catchblocks must follow each other, with no other statements or blocks in between. - A
tryblock without either acatchblock or afinallyblock is illegal (try-with-resources is the only exception). - The
finallyblock must immediately follow the lastcatchblock, or thetryblock if there is nocatch.
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
tryblock completes without exceptions,finallyruns immediately after it. - If an exception is thrown and caught,
finallyruns immediately after the matchingcatchblock completes. - If an exception is thrown and no
catchblock matches it,finallystill runs, and then the exception propagates to the caller. - If the
tryorcatchblock containsreturn, the return value is evaluated first, thenfinallyruns, and only then does the method actually return. - If an exception is thrown (or re-thrown) inside a
catchblock,finallyruns 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 everyjava.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
tryblock andclose()throw exceptions, the exception from thetryblock "wins"; the exceptions fromclose()are attached to it as suppressed and are available viagetSuppressed(). - Since Java 9, an existing
finalor 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