00:00

try-catch Block in Java – Explained with Examples

The try-catch block is Java's basic tool for handling exceptions. Code that might fail goes in the try block; code that responds to a specific failure goes in a catch block. If no exception occurs, the catch block is skipped. If one does, Java jumps straight to the matching catch block, and the program continues instead of crashing.

This tutorial covers the syntax, exactly how control flows through try-catch, how exception types are matched, the rules for ordering catch blocks, and how to rethrow or wrap exceptions.

1. Syntax

try-catch syntax
try {
    // code that may throw an exception
} catch (ExceptionType variableName) {
    // code that handles that exception
}
  • A try must be followed by at least one catch or a finally (or be a try-with-resources statement).
  • The catch parameter declares which exception type it handles, and gives you access to the exception object.
  • Variables declared inside try are not visible in catch; declare them before the try if both need them.

2. A First Example

Parsing user input is a classic use: if the text is not a number, fall back to a default instead of crashing.

TryCatchBasics.java
public class TryCatchBasics {
    static int parseQuantity(String text) {
        try {
            return Integer.parseInt(text.trim());
        } catch (NumberFormatException e) {
            System.out.println("  '" + text + "' is not a number, using 1");
            return 1;                                  // sensible fallback
        }
    }

    public static void main(String[] args) {
        for (String input : new String[] {"5", " 12 ", "two", ""}) {
            System.out.println("quantity = " + parseQuantity(input));
        }
    }
}
Output
quantity = 5
quantity = 12
  'two' is not a number, using 1
quantity = 1
  '' is not a number, using 1
quantity = 1

3. How Control Flows Through try-catch

  1. Statements in try run in order.
  2. If one throws an exception, the rest of the try block is skipped.
  3. Java looks for the first catch whose type matches the exception and runs it.
  4. Execution continues after the whole try-catch statement.
  5. If no catch matches, the exception propagates to the calling method.
ControlFlow.java
public class ControlFlow {
    public static void main(String[] args) {
        int[] stock = {5, 0, 3};
        for (int i = 0; i < stock.length; i++) {
            try {
                System.out.println("step A: item " + i);
                int perDay = 30 / stock[i];            // fails when stock is 0
                System.out.println("step B: lasts " + perDay + " days");
            } catch (ArithmeticException e) {
                System.out.println("caught: " + e.getMessage() + " (step B skipped)");
            }
            System.out.println("step C: after try-catch");
        }
    }
}
Output
step A: item 0
step B: lasts 6 days
step C: after try-catch
step A: item 1
caught: / by zero (step B skipped)
step C: after try-catch
step A: item 2
step B: lasts 10 days
step C: after try-catch

For item 1, "step B" was skipped, the catch block ran, and the loop continued normally with "step C".

4. Matching Exception Types

A catch block handles its declared type and all its subclasses. Catching IOException also catches FileNotFoundException; catching RuntimeException also catches NullPointerException:

CatchOrder.java
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class CatchOrder {
    public static void main(String[] args) {
        // A catch block for a parent type also catches its subclasses
        try {
            new FileReader("missing.txt").close();
        } catch (FileNotFoundException e) {          // most specific first
            System.out.println("File not found: " + e.getMessage());
        } catch (IOException e) {                     // then the more general type
            System.out.println("Other I/O problem: " + e.getMessage());
        }

        try {
            Object item = findItem("pen");
            System.out.println(item.toString());          // item is null
        } catch (RuntimeException e) {                // catches NullPointerException too
            System.out.println("Caught as RuntimeException: " + e.getClass().getSimpleName());
        }
    }

    static Object findItem(String name) { return null; }   // pretend nothing was found
}
Output
File not found: missing.txt (No such file or directory)
Caught as RuntimeException: NullPointerException

When several catch blocks are listed, the first match wins, so put specific exceptions before general ones. Putting a parent type first makes the child catch unreachable, and the compiler rejects it:

javac output
Bad.java:4: error: exception NumberFormatException has already been caught
 catch (NumberFormatException e) { }
 ^
1 error

More on this in Multiple catch Blocks.

5. Rethrowing and Wrapping Exceptions

Sometimes the right response is not to recover but to translate a low-level exception into one that makes sense at a higher level, while keeping the original as the cause:

WrapAndRethrow.java
import java.util.Map;

public class WrapAndRethrow {
    static class ConfigException extends RuntimeException {
        ConfigException(String message, Throwable cause) { super(message, cause); }
    }

    static int readPort(Map<String, String> config) {
        try {
            return Integer.parseInt(config.get("port"));
        } catch (NumberFormatException e) {
            // Translate a low-level error into one that makes sense to callers, keeping the cause
            throw new ConfigException("Invalid port in configuration", e);
        }
    }

    public static void main(String[] args) {
        try {
            readPort(Map.of("port", "80a"));
        } catch (ConfigException e) {
            System.out.println(e.getMessage());
            System.out.println("caused by: " + e.getCause());
        }
    }
}
Output
Invalid port in configuration
caused by: java.lang.NumberFormatException: For input string: "80a"

Always pass the original exception to the new one's constructor. Losing the cause makes problems very hard to diagnose.

6. What to Do in a catch Block

  • Recover: use a default value, retry, or ask the user again.
  • Report: log the exception with its stack trace and show a clear message.
  • Translate: wrap it in a more meaningful exception and rethrow.
  • Clean up partial work, then rethrow.
Never leave a catch block empty. A silently swallowed exception hides bugs and can leave your program in a broken state. If ignoring an exception is truly correct, add a comment explaining why, or name the variable ignored.

7. Rules and Tips

  • Keep try blocks small: only the statements that can throw the exception you are handling.
  • Catch the most specific exception you can handle.
  • Do not use try-catch for normal control flow, for example to detect the end of an array.
  • Catching a checked exception that the try block cannot throw is a compile error.
  • Use finally or try-with-resources for clean-up code that must always run.
  • Catch several unrelated types in one block with multi-catch: catch (IOException | SQLException e).

8. Summary

  • try wraps risky code; catch handles a specific exception type and its subclasses.
  • When an exception occurs, the rest of the try block is skipped and the first matching catch runs.
  • Order catch blocks from specific to general; the compiler rejects unreachable catches.
  • Rethrow or wrap exceptions, always keeping the original cause.
  • Never swallow exceptions silently.

Related Tutorials