DEV Community

Cover image for Java throw vs throws: The Exception Bug They Reveal
Mark Yu
Mark Yu

Posted on • Edited on

Java throw vs throws: The Exception Bug They Reveal

throw and throws are easy to memorize and still easy to misuse.

The real difference is not grammar.

throw is the moment you raise an exception. throws is the method contract that warns callers an exception may escape.

The Short Version

Keyword Where it appears What it means
throw inside method body raise this exception now
throws method signature this method may pass this exception upward

throw: Fail Here

public User findUser(String id) {
    if (id == null || id.isBlank()) {
        throw new IllegalArgumentException("User id is required");
    }

    return repository.findById(id);
}
Enter fullscreen mode Exit fullscreen mode

This is a direct failure.

I like this for invalid programmer input or impossible states. Do not let bad data travel deeper into the system.

throws: Caller Must Deal With It

public String readConfig(String path) throws IOException {
    return Files.readString(Path.of(path));
}
Enter fullscreen mode Exit fullscreen mode

The method is saying:

I might fail with IOException, and I am not handling it here.

Caller:

try {
    String config = readConfig("app.yml");
} catch (IOException e) {
    log.error("Failed to read config", e);
}
Enter fullscreen mode Exit fullscreen mode

Checked vs Runtime Exceptions

Checked exceptions must be handled or declared:

public void load() throws IOException {
    Files.readString(Path.of("app.yml"));
}
Enter fullscreen mode Exit fullscreen mode

Runtime exceptions do not need throws:

throw new IllegalArgumentException("Bad request");
Enter fullscreen mode Exit fullscreen mode

My rough rule:

  • Use checked exceptions when the caller can reasonably recover.
  • Use runtime exceptions for programming mistakes or invalid state.

Do Not Swallow Exceptions

This is the code smell:

try {
    sendEmail();
} catch (Exception e) {
    // ignore
}
Enter fullscreen mode Exit fullscreen mode

If the email matters, ignoring the exception lies to the system.

At least log it or return a failure result:

try {
    sendEmail();
} catch (MailException e) {
    log.warn("Email send failed for user {}", userId, e);
}
Enter fullscreen mode Exit fullscreen mode

API Boundary Example

Inside infrastructure code:

public User loadUser(String id) throws SQLException {
    // database call
}
Enter fullscreen mode Exit fullscreen mode

At the service boundary, I usually translate it:

public User getUser(String id) {
    try {
        return userDao.loadUser(id);
    } catch (SQLException e) {
        throw new UserLookupException("Failed to load user " + id, e);
    }
}
Enter fullscreen mode Exit fullscreen mode

This keeps SQL details from leaking everywhere.

Final Thought

throw is an action. throws is a warning label.

Good exception design is mostly about choosing where a failure should be handled and where it should be translated.

Do you prefer checked exceptions or runtime exceptions in Java service code?

Top comments (0)