Insights into Company
← Java

Exception Handling

Understand the idea. Compare it. Explain it. Test yourself.

Question 1 of 10

What is exception handling?

Solution and explanation

What it is

An exception is an event that disrupts the normal flow of a program at run time: a missing file, a network failure, dividing by zero, a null reference. Exception handling is the mechanism to detect such events, respond to them and keep the program running (or fail cleanly) instead of crashing.

Without handling

int[] marks = {80, 90};
System.out.println(marks[5]);       // ArrayIndexOutOfBoundsException → program stops
System.out.println("Never printed");

With handling

try {
    int amount = Integer.parseInt(input);          // may throw NumberFormatException
    double perHead = amount / members;             // may throw ArithmeticException
    System.out.println(perHead);
} catch (NumberFormatException e) {
    System.out.println("Please enter digits only");
} catch (ArithmeticException e) {
    System.out.println("Group must have at least one member");
} finally {
    System.out.println("Split calculation finished");   // always runs
}

The keywords

KeywordPurpose
tryWraps code that might throw
catchHandles a specific exception type
finallyRuns whether or not an exception occurred (cleanup)
throwThrows an exception object
throwsDeclares exceptions a method may pass to its caller

How it works

  1. An exception object is created with the type, message and stack trace.
  2. The JVM looks for a matching catch in the current method, then in its caller, up the call stack.
  3. If none is found, the thread terminates and the stack trace is printed.

Multi-catch (Java 7+)

catch (IOException | SQLException e) { log.error("Data access failed", e); }

Best practices

  • Catch specific exceptions, not Exception or Throwable blindly.
  • Never swallow exceptions with an empty catch block.
  • Log with the exception object to keep the stack trace.
  • Use try-with-resources for anything that must be closed.
  • In Spring REST APIs, map exceptions to HTTP responses centrally with @RestControllerAdvice.
Practice explaining this
Question 2 of 10

What is the difference between Error and Exception?

Solution and explanation

Short answer

Both extend Throwable. An Exception represents a condition an application can reasonably catch and recover from. An Error represents a serious problem in the JVM or environment that applications normally should not try to handle.

ExceptionError
Package / parentjava.lang.Exceptionjava.lang.Error
CauseApplication logic or external resourcesJVM or system resources
RecoverableUsually yesUsually no
Should you catch itYes, where you can handle itGenerally no
Checked?Checked (except RuntimeException subclasses)Always unchecked
ExamplesIOException, SQLException, NullPointerException, IllegalArgumentExceptionOutOfMemoryError, StackOverflowError, NoClassDefFoundError, ExceptionInInitializerError

Examples

// Exception: recoverable
try {
    Files.readString(Path.of("config.json"));
} catch (IOException e) {
    useDefaultConfig();
}

// Error: not recoverable in normal code
static int depth(int n) { return depth(n + 1); }   // StackOverflowError: fix the code, don't catch it
List<byte[]> leak = new ArrayList<>();
while (true) leak.add(new byte[10_000_000]);       // OutOfMemoryError: fix the leak or increase -Xmx

Practical notes

  • Catching Throwable or Error hides fatal conditions; the JVM may be in an inconsistent state.
  • Exceptions to that rule: top-level frameworks may catch Throwable to log it before the thread dies.
  • AssertionError (from assert) is also an Error.
Practice explaining this
Question 3 of 10

What is Throwable?

Solution and explanation

What it is

java.lang.Throwable is the root class of everything that can be thrown with throw and caught with catch. Only objects of Throwable or its subclasses can be thrown.

Hierarchy

Object
└── Throwable
    ├── Error                        (serious, unchecked)
    │   ├── OutOfMemoryError
    │   ├── StackOverflowError
    │   └── NoClassDefFoundError
    └── Exception                    (checked unless RuntimeException)
        ├── IOException
        │   └── FileNotFoundException
        ├── SQLException
        ├── InterruptedException
        └── RuntimeException         (unchecked)
            ├── NullPointerException
            ├── ArithmeticException
            ├── IllegalArgumentException
            │   └── NumberFormatException
            ├── IllegalStateException
            ├── IndexOutOfBoundsException
            └── ClassCastException

Useful methods

MethodReturns
getMessage()The detail message
getCause()The wrapped original exception, if any
printStackTrace()Prints type, message and call stack to stderr
getStackTrace()Array of stack frames
addSuppressed() / getSuppressed()Exceptions suppressed during try-with-resources closing
toString()Class name + message
try {
    repository.save(order);
} catch (DataAccessException e) {
    throw new OrderFailedException("Could not save order " + order.getId(), e);   // keep the cause
}

Exception chaining with a cause preserves the original stack trace ("Caused by: …" in logs), which is essential for debugging.

Practice explaining this
Question 4 of 10

What are checked and unchecked exceptions?

Solution and explanation

Checked exceptions

Subclasses of Exception that are not RuntimeException. The compiler checks them: a method must either catch them or declare them with throws. They represent foreseeable problems outside the program's control.

public String readResume(Path path) throws IOException {   // must declare or catch
    return Files.readString(path);
}

Examples: IOException, FileNotFoundException, SQLException, InterruptedException, ClassNotFoundException.

Unchecked exceptions

RuntimeException and its subclasses (plus Error). The compiler does not force you to handle them. They usually indicate programming bugs or invalid arguments.

public void setAge(int age) {
    if (age < 18) throw new IllegalArgumentException("Candidate must be 18+");   // no throws needed
}

Examples: NullPointerException, ArrayIndexOutOfBoundsException, ArithmeticException, IllegalArgumentException, IllegalStateException, NumberFormatException, ClassCastException.

Comparison

CheckedUnchecked
Compile-time enforcementYesNo
ParentException (not RuntimeException)RuntimeException / Error
Typical causeExternal: files, network, DBProgramming error, bad input
HandlingCatch or declarePrevent with checks; catch where meaningful

Which should a custom exception be?

  • Checked: if the caller can reasonably recover (e.g. InsufficientBalanceException the UI can show).
  • Unchecked: for programming errors or when recovery is not possible at that level. Modern frameworks (Spring, Hibernate) mostly use unchecked exceptions, e.g. Spring wraps SQLException into unchecked DataAccessException, because lambdas and streams do not work well with checked exceptions.
Practice explaining this
Question 5 of 10

What is the difference between throw and throws?

Solution and explanation

Short answer

  • throw actually throws one exception object, inside a method body.
  • throws is part of a method signature and declares the exception types the method may pass on to its caller.
public class WalletService {

    public void withdraw(double amount) throws InsufficientBalanceException {   // throws: declaration
        if (amount <= 0) {
            throw new IllegalArgumentException("Amount must be positive");        // throw: action
        }
        if (amount > balance) {
            throw new InsufficientBalanceException("Balance is only ₹" + balance);
        }
        balance -= amount;
    }
}

// Caller must handle the checked exception
try {
    wallet.withdraw(5000);
} catch (InsufficientBalanceException e) {
    showMessage(e.getMessage());
}
throwthrows
Statement inside a method or blockClause in a method/constructor signature
Followed by an exception instance: throw new X()Followed by class names: throws X, Y
Throws exactly one exception at a timeCan list several types
Transfers control immediatelyOnly informs the compiler and callers
Works with checked and uncheckedRequired for checked exceptions not caught inside

Rethrowing

catch (SQLException e) {
    log.error("Query failed", e);
    throw new ReportException("Report generation failed", e);   // wrap and rethrow
}

Overriding rule

An overriding method may declare the same checked exceptions, narrower ones or none, but not new or broader checked exceptions.

Practice explaining this
Question 6 of 10

Can try exist without catch?

Solution and explanation

Answer

Yes, a try block needs either at least one catch or a finally (or both). And try-with-resources can stand alone.

try–finally

Used when you do not handle the exception here, but must run cleanup code; the exception continues to the caller after finally runs.

lock.lock();
try {
    updateInventory();
} finally {
    lock.unlock();            // always released, even if updateInventory throws
}

try-with-resources alone

try (BufferedReader reader = Files.newBufferedReader(path)) {
    return reader.readLine();
}                              // reader closed automatically; exceptions propagate (method declares throws IOException)

Not allowed

try {
    doWork();
}                              // compile error: 'try' without 'catch', 'finally' or resource declarations

When does finally not run?

  • System.exit() is called inside try or catch.
  • The JVM crashes or the process is killed.
  • An infinite loop or deadlock in try.

Otherwise it runs even when try executes return.

Trick: return in finally

static int test() {
    try { return 1; }
    finally { return 2; }      // returns 2 and swallows any exception from try — avoid this
}
Practice explaining this
Question 7 of 10

What is try-with-resources?

Solution and explanation

What it is

Try-with-resources (Java 7) declares one or more resources in parentheses after try. Each resource is closed automatically at the end of the block, in reverse order of creation, whether the block completes normally or throws. A resource is any object implementing AutoCloseable.

Before Java 7

BufferedReader reader = null;
try {
    reader = new BufferedReader(new FileReader("jobs.csv"));
    System.out.println(reader.readLine());
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (reader != null) {
        try { reader.close(); } catch (IOException ignored) { }   // noisy and easy to get wrong
    }
}

With try-with-resources

try (BufferedReader reader = Files.newBufferedReader(Path.of("jobs.csv"))) {
    System.out.println(reader.readLine());
} catch (IOException e) {
    log.error("Could not read jobs.csv", e);
}

Several resources (JDBC)

String sql = "SELECT title FROM jobs WHERE city = ?";
try (Connection con = dataSource.getConnection();
     PreparedStatement ps = con.prepareStatement(sql)) {
    ps.setString(1, "Pune");
    try (ResultSet rs = ps.executeQuery()) {
        while (rs.next()) System.out.println(rs.getString("title"));
    }
}   // closes rs, then ps, then con

Suppressed exceptions

If the block throws and close() also throws, the exception from the block is kept as the main one and the close exception is attached as suppressed (e.getSuppressed()), so no information is lost. In the old finally style, the close exception would hide the real one.

Java 9 improvement

An effectively final variable declared earlier can be used directly:

BufferedReader reader = Files.newBufferedReader(path);
try (reader) { … }
Practice explaining this
Question 8 of 10

What is AutoCloseable?

Solution and explanation

What it is

java.lang.AutoCloseable is an interface with a single method, void close() throws Exception. Any class implementing it can be used in a try-with-resources statement, which calls close() automatically.

Closeable vs AutoCloseable

AutoCloseableCloseable
Packagejava.lang (Java 7)java.io (Java 5)
close() throwsExceptionIOException
Idempotent close requiredRecommendedYes (calling close twice has no effect)
RelationshipParentCloseable extends AutoCloseable

Implemented by streams, readers/writers, JDBC Connection/Statement/ResultSet, Scanner, sockets, ExecutorService (Java 19+), and many more.

Your own resource

public class AuditSession implements AutoCloseable {
    private final String user;

    public AuditSession(String user) {
        this.user = user;
        System.out.println("Audit started for " + user);
    }

    public void record(String action) { System.out.println(user + " did " + action); }

    @Override
    public void close() {                       // narrower: throws nothing
        System.out.println("Audit closed for " + user);
    }
}

try (AuditSession audit = new AuditSession("admin")) {
    audit.record("deleted job 42");
}
Audit started for admin
admin did deleted job 42
Audit closed for admin

Tip: declare close() with the narrowest exception possible (or none), so callers do not need to catch a generic Exception.

Practice explaining this
Question 9 of 10

How do you create a custom exception?

Solution and explanation

Steps

  1. Extend Exception (checked) or RuntimeException (unchecked).
  2. Give it a meaningful name ending in Exception.
  3. Provide constructors for a message and for a message plus cause.
  4. Optionally add fields with extra context.
public class InsufficientBalanceException extends RuntimeException {
    private final double balance;
    private final double requested;

    public InsufficientBalanceException(double balance, double requested) {
        super(String.format("Requested ₹%.2f but balance is ₹%.2f", requested, balance));
        this.balance = balance;
        this.requested = requested;
    }

    public InsufficientBalanceException(String message, Throwable cause) {
        super(message, cause);
        this.balance = 0;
        this.requested = 0;
    }

    public double getShortfall() { return requested - balance; }
}

Throwing and handling

public void withdraw(double amount) {
    if (amount > balance) throw new InsufficientBalanceException(balance, amount);
    balance -= amount;
}

try {
    account.withdraw(10000);
} catch (InsufficientBalanceException e) {
    System.out.println(e.getMessage() + ". Add ₹" + e.getShortfall());
}

In a Spring Boot API

@ResponseStatus(HttpStatus.NOT_FOUND)
public class JobNotFoundException extends RuntimeException {
    public JobNotFoundException(long id) { super("Job " + id + " not found"); }
}

@RestControllerAdvice
public class ApiErrors {
    @ExceptionHandler(InsufficientBalanceException.class)
    ResponseEntity<Map<String, String>> balance(InsufficientBalanceException e) {
        return ResponseEntity.unprocessableEntity().body(Map.of("error", e.getMessage()));
    }
}

Good practice

  • Create custom exceptions for business rules, not for every small case.
  • Always keep the cause when wrapping.
  • Group related ones under a base class (PaymentException → CardDeclinedException, UpiTimeoutException).
Practice explaining this
Question 10 of 10

What is exception propagation?

Solution and explanation

What it is

When an exception is thrown and not caught in the current method, the method stops and the exception is passed (propagated) to its caller, then to that method's caller, and so on down the call stack, until some method catches it. If none does, the thread ends and the JVM prints the stack trace.

public class Checkout {
    void validateCard(String card) {
        if (card.length() != 16) throw new IllegalArgumentException("Invalid card number");   // thrown here
    }
    void pay(String card)        { validateCard(card); }        // not caught → propagates
    void placeOrder(String card) { pay(card); }                 // not caught → propagates

    public static void main(String[] args) {
        try {
            new Checkout().placeOrder("1234");
        } catch (IllegalArgumentException e) {                   // caught here
            System.out.println("Order failed: " + e.getMessage());
        }
    }
}
Call stack when thrown          Propagation
main                            ▲ caught in main
 └ placeOrder                   │
    └ pay                       │
       └ validateCard  ── throw ┘

Checked vs unchecked

  • Unchecked exceptions propagate automatically.
  • Checked exceptions propagate only if each method on the way declares them with throws; otherwise compilation fails.

Reading a stack trace

java.lang.IllegalArgumentException: Invalid card number
    at Checkout.validateCard(Checkout.java:3)    ← where it was thrown
    at Checkout.pay(Checkout.java:5)
    at Checkout.placeOrder(Checkout.java:6)
    at Checkout.main(Checkout.java:10)           ← where the chain started

Design guideline

Catch an exception at the level that can actually do something about it (retry, show a message, return an HTTP error). Lower layers should let it propagate or wrap it with context.

Practice explaining this