Question 4 · Section 7

Which exceptions must be handled?

The Java compiler forces you to handle checked exceptions. These are all subclasses of Exception, except RuntimeException.

Language versions: English Russian Ukrainian

Junior Level

Checked exceptions - mandatory handling

The Java compiler forces you to handle checked exceptions. These are all subclasses of Exception, except RuntimeException.

Checked exceptions are a contract between a method and its caller. The method says: “I might encounter X, and you must be prepared for it.”

// Mandatory: either try-catch or throws
public void readFile() throws IOException {
    Files.readAllLines(Paths.get("file.txt"));
}

// Or handle on the spot
public void safeRead() {
    try {
        Files.readAllLines(Paths.get("file.txt"));
    } catch (IOException e) {
        log.error("Failed to read file", e);
    }
}

Common exceptions to handle

  • IOException - file and network operations
  • SQLException - database operations
  • ClassNotFoundException - class loading
  • InterruptedException - multithreading

Unchecked exceptions - optional handling

NullPointerException, IllegalArgumentException and other RuntimeExceptions don’t need to be handled. They signal errors in code.

Basic rule

  • Checked - external failures (files, network, DB) -> must handle
  • Unchecked - programming errors -> must fix the code

Middle Level

Type Obligation Who requires
Checked Must handle or declare throws Compiler
Unchecked Recommended to handle Architecture/team
Error Should not catch JVM (recovery impossible)

Compiler-level check

At the JVM level, there is no separation between checked and unchecked. The athrow instruction simply throws an object. The mandatory handling is only a javac check.

Exceptions attribute in class file

When you declare void method() throws IOException, an Exceptions attribute appears in the .class file. The compiler uses it for checking. Other languages (Kotlin, Groovy) ignore this attribute.

Sneaky Throws - bypassing the check

You can throw a checked exception without throws:

@SuppressWarnings("unchecked")
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
    throw (E) e; // Type erasure deceives the compiler
}

// Lombok does this automatically
@SneakyThrows
public void readFile() {
    // IOException without throws!
    Files.readAllLines(Paths.get("file.txt"));
}

Exception Translation

The correct approach - catch checked exceptions at the layer boundary and translate to domain unchecked:

public interface Storage {
    byte[] read(String path); // Does not depend on SQL
}

public class SqlStorage implements Storage {
    public byte[] read(String path) {
        try {
            return jdbcTemplate.queryForObject(...);
        } catch (SQLException e) {
            throw new StorageException("Failed to read: " + path, e);
        }
    }
}

Senior Level

Functional programming and checked exceptions

Standard interfaces (Function, Predicate, Supplier) don’t support checked exceptions:

// Will not compile
stream.map(path -> Files.readString(path))

// Solution 1 - wrapper
stream.map(path -> {
    try { return Files.readString(path); }
    catch (IOException e) { throw new UncheckedIOException(e); }
})

// Solution 2 - Sneaky Throws
stream.map(path -> sneakyRead(path))

Performance

Never use checked exceptions for control flow. Throwing an exception - creating an object + walking the stack - is 100-1000x slower than returning Optional.

Input data validation

For invalid data, always use unchecked exceptions (IllegalArgumentException). This is a programmer error, not an external world issue.

InterruptedException is the exception that confirms the rule. This is not “control flow” in the usual sense, but a mechanism for cooperative thread cancellation. Its handling is part of the multithreading contract.

UncaughtExceptionHandler

If a checked exception “leaks” through sneaky throws and is not caught, it reaches the thread handler. Set a global handler:

Thread.setDefaultUncaughtExceptionHandler((thread, e) -> {
    log.error("Uncaught exception in thread {}", thread.getName(), e);
});

Empty Catch Blocks - the worst crime

catch (IOException e) {} // Crime!

If you must handle but don’t know how - at least log:

catch (IOException e) {
    log.error("IO error occurred", e);
    throw new RuntimeException(e);
}

Diagnostics

  • Static Analysis - SonarQube blocks empty catch blocks
  • Micrometer - track exception counts through metrics
  • Log Correlation - keep Trace ID for all error types

Interview Cheat Sheet

Must know:

  • Checked exceptions - must handle or declare throws (compiler requires)
  • Unchecked - recommended to handle at architecture level (not compiler)
  • Error - should NOT be caught (JVM in unstable state)
  • At JVM level there is no separation - athrow instruction throws everything the same way
  • Sneaky throws - bypass compiler check via type erasure
  • Exception translation - catch checked at layer boundary -> translate to domain unchecked
  • For invalid input data always use unchecked (IllegalArgumentException)
  • Empty catch blocks - the worst crime: at minimum log

Frequent follow-up questions:

  • How does JVM handle checked vs unchecked? - The same; separation is only in javac
  • What are sneaky throws? - Bypassing check via @SneakyThrows (Lombok) or generic erasure
  • Why is InterruptedException special? - It’s a cooperative thread cancellation mechanism, need to restore interrupt status
  • What to do if you don’t know how to handle? - Log and rethrow as RuntimeException

Red flags (NOT to say):

  • “I leave empty catch blocks if I don’t know what to do” - at least log
  • “I catch Error to prevent the app from crashing” - JVM is unstable, it’s meaningless
  • “Checked exceptions are the same as unchecked” - no, the compiler only checks checked

Related topics:

  • [[What is a checked exception and when to use it]]
  • [[What is an unchecked exception (Runtime Exception)]]
  • [[What is the difference between Error and Exception]]
  • [[Why you should not swallow exceptions (catch empty)]]
  • [[What are suppressed exceptions]]