Question 21 · Section 7

What does the throws keyword do?

throws - a keyword in the method signature that documents the contract: it tells the calling code which checked exceptions the method can throw and which the calling code must b...

Language versions: English Russian Ukrainian

Junior Level

Definition

throws - a keyword in the method signature that documents the contract: it tells the calling code which checked exceptions the method can throw and which the calling code must be prepared to handle.

public void readFile() throws IOException {
    Files.readAllLines(Paths.get("file.txt"));
}

What it means

The method tells the caller: “I may not succeed, and you must know about it”.

The calling code must:

  1. Wrap the call in try-catch
  2. Or also add throws to its signature
// Option 1: try-catch
public void safeRead() {
    try {
        readFile();
    } catch (IOException e) {
        log.error("Failed", e);
    }
}

// Option 2: propagate further
public void process() throws IOException {
    readFile();
}

Multiple exceptions

public void process() throws IOException, SQLException {
    // Can throw both
}

Why throws is needed for checked but not for unchecked

Checked exceptions (inherit Exception, but not RuntimeException) - compiler requires handling. throws is a way for the method to delegate responsibility to the caller: “I don’t know how to handle this error, let the caller decide”.

Unchecked exceptions (inherit RuntimeException) - compiler does not require handling. They represent programming errors (NullPointerException, IllegalArgumentException) that cannot be meaningfully handled - they need to be fixed, not caught. That’s why throws is not needed for them.

// Checked: compiler forces declaration or catch
public void readFile() throws IOException { ... }

// Unchecked: compiler doesn't require throws
public void process(String s) {
    if (s == null) throw new NullPointerException();
}

Unchecked exceptions and throws

For RuntimeException, throws is technically not needed, although the language allows writing it:

// No need to write throws NullPointerException
public void process(String s) {
    if (s == null) throw new NullPointerException();
}

Although void method() throws RuntimeException compiles, it’s considered bad style - unchecked exceptions should not clutter the signature.

When NOT to use throws

  1. In public library APIs - throws Exception forces the client to catch “everything”
  2. In interfaces with multiple checked exceptions - sign of SRP violation, split the interface
  3. In Spring REST controllers - use @ControllerAdvice instead of throws in each method
  4. In lambda expressions - functional interfaces don’t support checked exceptions
  5. For programming errors - IllegalArgumentException, NullPointerException are not declared via throws

Middle Level

Bytecode: Exceptions attribute

In the .class file, information about throws is stored in the method’s Exceptions attribute. JVM doesn’t use it for runtime control - only for compiler verifier.

When called through Reflection, JVM won’t force catching exceptions:

Method m = MyClass.class.getMethod("readFile");
m.invoke(obj); // IOException doesn't have to be caught

Binary Compatibility

Adding a checked exception breaks binary compatibility:

// Was:
public void process() { }

// Became - all clients stop compiling
public void process() throws IOException { }

Removing exception - binary compatible (old try-catch will continue working).

Senior Tip: if you need to add validation to a public API, use RuntimeException or create an overloaded method.

Overriding and LSP

Liskov Substitution Principle:

interface Service {
    void process() throws IOException;
}

class Impl implements Service {
    @Override
    // CAN: not throw at all (narrowing contract)
    public void process() { }

    @Override
    // CAN: throw a subtype
    public void process() throws FileNotFoundException { }

    // CANNOT: new checked exception - compilation error
    // public void process() throws IOException, SQLException { }
}

Unchecked and throws

Technically you can write void method() throws RuntimeException, but it’s bad style. Unchecked exceptions should not clutter the signature. For documentation, use @throws in JavaDoc.


Senior Level

Throws as documentation

Consider throws as part of the contract. throws InsufficientFundsException says more about business logic than any comment.

Generic Throws

Java allows generics:

public <E extends Exception> void doWork(ThrowingRunnable<E> task) throws E {
    task.run();
}

Actively used in functional interfaces and wrappers.

Interface Segregation

If an interface throws too many different throws - sign of SRP violation. Method does too many things.

// Bad - too many responsibilities
public interface Service {
    void process() throws IOException, SQLException, AuthException, ValidationException;
}

// Good - split into interfaces
public interface DataService {
    void save() throws DataAccessException;
}
public interface AuthService {
    void authenticate() throws AuthException;
}

// Even better - in Spring use @ControllerAdvice
@RestController
public class OrderController {
    // throws not needed - @ControllerAdvice will catch
    @PostMapping("/orders")
    public Order createOrder(@RequestBody OrderRequest req) {
        return orderService.create(req);
    }
}

ReflectiveOperationException

On Reflection, all checked exceptions of the called method are wrapped in InvocationTargetException.

throws Exception - undesirable pattern

In most cases, don’t write throws Exception in public API. It forces the client to catch “everything”, depriving the ability to adequately react to specific failures.

Diagnostics

  • javap -v MyClass.class - you’ll see the Exceptions attribute in bytecode
  • Binary compatibility check - tools like JAPICC check version compatibility
  • Static Analysis - Sonar blocks throws Exception in public APIs

Interview Cheat Sheet

Must know:

  • throws documents the method contract - which checked exceptions the caller must handle
  • Checked exceptions (not RuntimeException) compiler requires to declare or catch
  • Unchecked exceptions (RuntimeException) don’t require throws - these are programming errors
  • On overriding you can narrow throws (remove or replace with subtype), but not expand
  • Adding a checked exception breaks binary compatibility - clients will stop compiling
  • In Spring REST use @ControllerAdvice instead of throws in each method
  • throws Exception in public API - anti-pattern, forces client to catch “everything”

Frequent follow-up questions:

  • Must checked exceptions be caught? - Yes, either try-catch or your own throws
  • Can you add throws RuntimeException? - Technically yes, but bad style
  • What happens on method override? - Can remove throws or narrow, but not expand (LSP)
  • How is throws stored in bytecode? - Exceptions attribute in .class, JVM doesn’t check at runtime

Red flags (NOT to say):

  • “I write throws Exception everywhere for simplicity” - anti-pattern, client can’t handle adequately
  • “I don’t declare checked exceptions - compiler won’t notice” - won’t compile
  • throws affects runtime performance” - no, it’s only a compiler check
  • “Can add new checked exception without consequences” - breaks binary compatibility

Related topics:

  • [[21. Can you throw a checked exception from a method without throws]] - bypass via sneaky throws
  • [[2. What is a checked exception and when to use it]] - nature of checked exceptions
  • [[3. What is an unchecked exception (Runtime Exception)]] - why they don’t require throws
  • [[27. Can you rethrow an exception]] - rethrow preserving throws
  • [[13. Can you create custom exceptions]] - custom exceptions for throws