Exceptions

Things go wrong at run time: a file is missing, the network drops, a user types letters where a number belongs. Java reports such problems by throwing an exception, which travels up the call stack until some code catches it — or the program stops with a stack trace. Handling exceptions well is what makes programs robust.

An uncaught exception

JAVA
public class Main {
    static int average(int[] values) {
        int sum = 0;
        for (int v : values) sum += v;
        return sum / values.length;
    }

    public static void main(String[] args) {
        System.out.println(average(new int[]{4, 8}));
        System.out.println(average(new int[]{}));
        System.out.println("never printed");
    }
}
Output
6
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Main.average(Main.java:5)
    at Main.main(Main.java:10)

The stack trace shows the exception type, its message, and the chain of method calls — read it from the top: the problem happened in average at line 5, called from main at line 10.

try and catch

JAVA
public class Main {
    public static void main(String[] args) {
        String[] inputs = {"42", "seven", "8"};
        int total = 0;
        for (String input : inputs) {
            try {
                total += Integer.parseInt(input);
            } catch (NumberFormatException e) {
                System.out.println("Skipping '" + input + "': " + e.getMessage());
            }
        }
        System.out.println("Total: " + total);
    }
}
Output
Skipping 'seven': For input string: "seven"
Total: 50

Code that might fail goes in try. If an exception of the matching type is thrown, execution jumps to the catch block, then continues after it. The exception object carries a message and the stack trace.

Several catch blocks

JAVA
public class Main {
    static String lookup(String[] items, String indexText) {
        try {
            int index = Integer.parseInt(indexText);
            return items[index].toUpperCase();
        } catch (NumberFormatException e) {
            return "not a number";
        } catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
            return "no such item (" + e.getClass().getSimpleName() + ")";
        }
    }

    public static void main(String[] args) {
        String[] items = {"apple", null, "cherry"};
        System.out.println(lookup(items, "2"));
        System.out.println(lookup(items, "x"));
        System.out.println(lookup(items, "9"));
        System.out.println(lookup(items, "1"));
    }
}
Output
CHERRY
not a number
no such item (ArrayIndexOutOfBoundsException)
no such item (NullPointerException)

Catch blocks are checked in order; A | B handles several types in one block. Catch specific types rather than Exception, so you don't accidentally hide bugs.

finally

JAVA
public class Main {
    static int divide(int a, int b) {
        try {
            return a / b;
        } finally {
            System.out.println("finally runs for " + a + "/" + b);
        }
    }

    public static void main(String[] args) {
        System.out.println(divide(10, 2));
        try {
            divide(1, 0);
        } catch (ArithmeticException e) {
            System.out.println("caught: " + e.getMessage());
        }
    }
}
Output
finally runs for 10/2
5
finally runs for 1/0
caught: / by zero

A finally block always runs — after normal completion, after a return, and after an exception — so it's used for clean-up. For closing resources, try-with-resources (next lesson) is better.

Throwing exceptions

JAVA
public class Main {
    static double withdraw(double balance, double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive: " + amount);
        }
        if (amount > balance) {
            throw new IllegalStateException("Insufficient funds");
        }
        return balance - amount;
    }

    public static void main(String[] args) {
        System.out.println(withdraw(100, 30));
        try {
            withdraw(100, 500);
        } catch (IllegalStateException e) {
            System.out.println("Refused: " + e.getMessage());
        }
    }
}
Output
70.0
Refused: Insufficient funds

throw raises an exception. IllegalArgumentException (bad argument) and IllegalStateException (object not in the right state for this call) are the standard choices for rejecting invalid use.

Checked and unchecked exceptions

KindExamplesRule
Checked (extend Exception)IOException, SQLExceptionMust be caught or declared with throws
Unchecked (extend RuntimeException)NullPointerException, IllegalArgumentExceptionNo requirement — usually programming errors
Errors (extend Error)OutOfMemoryError, StackOverflowErrorSerious problems; don't catch them
JAVA
import java.nio.file.Files;
import java.nio.file.Path;

public class Main {
    static String readConfig() {
        return Files.readString(Path.of("config.txt"));
    }

    public static void main(String[] args) {
    }
}
Compiler error
Main.java:6: error: unreported exception IOException; must be caught or declared to be thrown

Checked exceptions represent problems a correct program must still expect, like missing files. The compiler forces you to handle them — either with try/catch or by adding throws IOException to the method so the caller deals with it.

JAVA
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

public class Main {
    static String readConfig() throws IOException {
        return Files.readString(Path.of("config-that-does-not-exist.txt"));
    }

    public static void main(String[] args) {
        try {
            System.out.println(readConfig());
        } catch (IOException e) {
            System.out.println("Using defaults: " + e.getClass().getSimpleName());
        }
    }
}
Output
Using defaults: NoSuchFileException

Good practice

  • Never swallow exceptions with an empty catch {}. At least log them.
  • Catch where you can do something useful — retry, use a default, show a message. Otherwise let it propagate.
  • Fail fast: validate arguments at the start of methods.
  • Don't use exceptions for normal control flow; check conditions instead when you can.

Try it yourself

Parse a list of ages, collecting errors instead of stopping at the first one.

Try it yourself · Main.java
Edit the code, copy it into Main.java and run java Main.java (Java 25+). How to set up Java
Output
'-2' out of range: -2
'abc' is not a number
3 valid ages