Java Programming

Exceptions, Custom Exceptions and Resource Management

PGCP-BDA

exception hierarchy

Python exceptions derive from BaseException; application errors normally derive from Exception and may be caught by specificity.

checked and unchecked exceptions

Checked exceptions must be caught or declared, while unchecked exceptions need not be declared

try and catch

A try block encloses operations that may fail and compatible catch clauses handle exceptions from most specific to more general types.

multiple catch

Multiple catch clauses separate recovery by exception type, while a multi-catch clause shares one handler for unrelated alternatives.

throw and throws

throw creates an exceptional transfer with one Throwable object; throws declares exception types a method may propagate.

finally

finally normally runs after try and catch for cleanup, but try-with-resources is safer for AutoCloseable resources and preserves suppressed failures.

custom exception

A custom Java exception gives a domain failure a precise type and may carry structured context while preserving the original cause.

try-with-resources

Try-with-resources closes declared AutoCloseable resources in reverse order and records close failures as suppressed exceptions when another failure is.

suppressed exception

An additional exception retained when resource closing fails while another exception is already leaving a try block.

Custom exceptions and causes

A domain-specific exception gives a failure meaningful vocabulary:

class OrderImportException extends Exception {
    OrderImportException(String message, Throwable cause) {
        super(message, cause);
    }
}

void importOrders(Path path) throws OrderImportException {
    try {
        parse(Files.readString(path));
    } catch (IOException ex) {
        throw new OrderImportException(
                "Could not read order file: " + path.getFileName(), ex);
    }
}

The wrapper describes the higher-level operation, while the cause retains the original type, message and stack trace. This is exception translation, not information loss. Choose checked or unchecked inheritance based on whether callers are expected and able to recover.

The exception hierarchy

Throwable is the root of values that Java can throw and catch. Its main branches are Error and Exception.

  • Error describes serious JVM or environment failures, such as OutOfMemoryError. Application code usually does not recover from them.
  • Checked exceptions are Exception subclasses outside RuntimeException, such as IOException. The compiler generally requires them to be caught or declared.
  • Unchecked exceptions are RuntimeException and its subclasses, such as NullPointerException, IllegalArgumentException and NumberFormatException. The compiler does not require catch-or-declare handling.

Checked exceptions commonly represent anticipated external failures from which a caller might recover. Unchecked exceptions commonly represent invalid arguments, invalid state or programming defects. This is a design convention supported by compiler rules, not a claim that every unchecked failure is unrecoverable.

Suppressed exceptions

Both the try body and close() can fail. Try-with-resources preserves the body's exception as primary and attaches the close failure as a suppressed exception:

catch (IOException ex) {
    for (Throwable suppressed : ex.getSuppressed()) {
        logger.error("Cleanup also failed", suppressed);
    }
}

This prevents cleanup failure from erasing the more relevant operational failure. In contrast, careless manual closing in a finally block can replace the original exception.

Finally and control flow

A finally block normally runs as execution leaves its associated try or catch, whether by completion, return, break, continue or exception:

try {
    return calculate();
} finally {
    audit();
}

It is not an absolute guarantee: abrupt JVM termination, a system crash or a process kill can prevent it. Never put a return in finally; it can replace a pending return value or suppress an exception. Manual resource closing in finally is also verbose and error-prone, so use try-with-resources for closable resources.

Try and catch matching

try {
    int count = Integer.parseInt(input);
    process(count);
} catch (NumberFormatException ex) {
    System.out.println("Enter a whole number");
} catch (IllegalArgumentException ex) {
    System.out.println("Value is outside the accepted range");
}

The first compatible catch executes. Therefore, catch clauses must go from specific types to general types. Placing a superclass first makes a later subclass catch unreachable and causes compilation to fail.

A multi-catch handles alternatives identically:

try {
    loadConfiguration();
} catch (IOException | SecurityException ex) {
    report(ex);
}

Alternatives cannot have a subclass relationship because the narrower type would be redundant. The multi-catch variable is implicitly final.

Worked exception trace

static int example() {
    try {
        throw new IllegalStateException("body");
    } finally {
        System.out.println("cleanup");
    }
}

The exception begins to propagate from the try block. Before the method exits, finally prints cleanup. Because finally completes normally, the original IllegalStateException continues to the caller. If finally threw another exception or returned, it could replace the original outcome, which is why finally should contain careful cleanup rather than outcome-changing control flow.

Stack unwinding and propagation

When an exception is thrown, normal execution stops. The JVM searches for a matching handler in the current method. If none exists, that method's frame is unwound and the search continues at its caller. This continues until a handler is found or the exception reaches the thread's uncaught-exception mechanism.

The stack trace records the path through method calls and usually identifies the throw location. Catch an exception only where code can recover, add useful context, translate it at an abstraction boundary or perform required reporting. Catching and ignoring it loses evidence while allowing potentially invalid execution to continue.

Continue learning

Related notes

Put this topic into timed practice

Open mock tests when you want full-exam pacing, or keep drilling in practice mode.