Concepts / Declaring Exceptions with the throws Clause

Declaring Exceptions with the throws Clause

Exceptions whose classes are not subclasses of Error or RuntimeException are checked exceptions, and the compiler requires the program to handle them.

  • Programming
Interactive lab

Try it: try / except / else / finally

How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.

How it works

  1. Statements in try run until one raises an exception; the rest of try is skipped.
  2. Python checks the except clauses in order and runs the first one whose type matches.
  3. If nothing was raised, the else block runs instead.
  4. finally runs in every case.

Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.

Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.

Educational simulation

Loading the simulation…

A Promise Written in the Header

Most people who start programming in Java learn that an exception is a signal that something has gone wrong, and that a program can respond to the signal with a try statement and a catch clause. From there it is tempting to conclude that exception handling is a matter of taste: you catch the problems you expect and let the rest go. That picture is accurate for some exceptions and badly wrong for others. Java distinguishes between exceptions that a programmer may choose to handle and exceptions that the program must handle, and the compiler enforces that difference. Getting the distinction right is what separates code that compiles from code that the compiler rejects.

This article is about one specific mechanism for dealing with the mandatory cases. A subroutine can announce, in its own header, the exception classes it might throw. That announcement is called a throws clause. Before you read further, make a prediction, because the trace later in the article will test it.

What do you think happens?

Suppose a method header ends with throws IOException. Does that clause, by itself, cause an IOException to be thrown every time the method is called?

  • Yes, every call throws the exception

  • No, the clause only announces that the method might throw it

Reveal answer
Answer:

No, the clause only announces that the method might throw it

A throws clause does not throw anything by itself. The exception is actually thrown by a throw statement in the body, typically when a precondition is violated. The clause tells callers that a failure is possible and that they must deal with it.

Following a Checked Exception Up the Call Chain

Consider a small program with a method named readRecord. Its job is to read data from outside the program, and reading from outside can fail, for example when the data is unavailable. Because the failure is a checked exception, the method's header announces it with a throws clause. The body of the method throws the exception with a throw statement when the path it was given is unusable. The example is an original illustration written to show the two routes working together.

java
Output
record from data.txt
Notes on the code
  • The throws IOException clause in the header announces the possible failure to every caller
  • The throw statement in the body is what actually raises the exception, and only when the path is empty
  • The try statement in main is the handler that satisfies the compiler for this call

In the example above the path is not empty, so no exception is thrown and the output is the normal record line. The important question is what happens when a checked exception is thrown and nobody in the chain catches it. The diagram traces that case. Here main calls process, process calls readRecord, and readRecord throws IOException. Control leaves readRecord at once, so the rest of its body is skipped. Control then leaves process, which is also skipped, because there is no handler in it. Control then reaches main, and if main has no matching catch clause either, the program stops.

maincalls processprocesscalls readRecordreadRecordthrows IOExceptionProgram stopsno matching catchcallcallunwindsunwindsno handler
After readRecord throws IOException, which frames are skipped and where does execution end when no frame has a matching catch clause?

Checked and Unchecked Exceptions in the Class Hierarchy

Definition

Exceptions whose classes are not subclasses of Error or RuntimeException are checked exceptions, and the compiler requires the program to handle them.

Notice what this definition is about. It is not about how serious a problem is. The category is defined by where the exception class sits in Java's family of exception classes. All exception classes descend from Throwable. One branch of that family is Error, and another is RuntimeException. A class outside those two branches is a checked exception, and checked exceptions carry the mandatory requirement. Exceptions such as IllegalArgumentException and NullPointerException sit in the unchecked group, so the programmer may catch them or ignore them. IOException is outside those branches, so it is checked.

ThrowableErrorRuntimeExceptionIllegalArgumentExceptionExceptionIOExceptionoutside Error andRuntimeExceptionNullPointerExceptionextendsextendsextendsextendsextendsextends
Where do Error, RuntimeException, and the checked exception classes sit under Throwable, and which branch of that hierarchy makes an exception checked?

The branch that matters for this article is the one that falls outside Error and RuntimeException. Reading the diagram from the top, you can locate any exception class by asking one question: does its ancestry pass through Error or RuntimeException? If it does, the exception is unchecked. If it does not, the exception is checked, and the compiler will insist that the code either handles it or announces it.

The source gives a reason for this design. Checked exceptions generally represent conditions outside the control of the programmer, such as bad input or an illegal action taken by the user. Such errors cannot be prevented by writing better code alone, so a robust program has to be ready for them. Java's design makes it impossible for programmers to ignore the possibility of these errors. Unchecked exceptions, by contrast, are signals about a routine's inputs. A calling program is free either to catch an IllegalArgumentException or to ignore it, and declaring one in a throws clause is a courtesy to readers of the code rather than a compiler requirement.

Why Input and Output Make the Rule Unavoidable

Several checked exceptions can occur when using Java's input and output routines. The practical consequence is that you cannot use these routines comfortably until you understand something about exception handling. Input and output is where data from outside the program arrives, and that data may be malformed or unavailable. That is exactly the kind of condition checked exceptions are designed to represent. A program that reads a file cannot promise that the file exists, so the compiler asks the programmer to decide, explicitly, what happens when it does not.

Two Ways to Satisfy the Compiler

Suppose a statement inside a subroutine can generate a checked exception. That statement might be a throw statement that raises the exception directly, or it might be a call to another subroutine that can raise it. Either way, the exception must be handled. The source describes two routes. The first is a try statement with a catch clause, which handles the exception at the place where it happens. The second is a throws clause in the subroutine heading, which announces the exception and passes the obligation to whoever calls the subroutine.

Before
static String readRecord(String path) {
    if (path.isEmpty()) {
        throw new IOException("empty path");
    }
    return "record from " + path;
}

The header has no throws clause, so the compiler rejects the throw statement because the checked IOException is neither caught nor declared

After
static String readRecord(String path) throws IOException {
    if (path.isEmpty()) {
        throw new IOException("empty path");
    }
    return "record from " + path;
}

The header announces IOException, so the compiler accepts the method and the obligation moves to callers

The only change between the two versions is one clause in the heading. The body is identical. This shows that the requirement is about announcing and handling the exception, not about changing what the method does. The compiler's check is what enforces the rule. Note that the first version does not compile for a checked exception, so the throws clause is not optional decoration: it is the declaration that makes the throw legal.

Handled inthe same method bodyRequirement met bya catch clauseHandled insome caller, laterRequirement met bya declaration in the headerthenthen
How do a try statement with a catch clause and a throws clause in the subroutine heading differ in where the exception is handled and where the compiler's requirement is met?

Reading a Throws Clause in a Header

The clause is placed in the subroutine header, after the parameter list and before the body. Its form is the keyword throws followed by an exception class name. The example comes from the source material: a method named root that takes three double parameters and returns a double. Its header ends with a clause naming IllegalArgumentException. Read the header from left to right. The modifiers static and public come first, then the return type double, then the method name and its parameter list. The throws clause sits at the far right, outside the parentheses, and names the single exception class the routine may throw. Nothing inside the parentheses changes when a throws clause is added.

staticmodifierpublicmodifierdoublereturn typerootmethod name(double a, double b,double c)three parametersthrowsstarts the clauseIllegalArgumentExceptiondeclared exception class
Which part of a method header is the throws clause, and which exception class names does it declare?

A single header can name more than one exception type, and the source states that the types are separated by commas. The following header is a generated illustration of that form. It uses placeholder class names rather than real library types, so only the shape matters here.

java
Notes on the code
  • The comma-separated list declares two exception classes
  • Each name is a promise that the method might throw that type

What does a multi-type declaration promise? It tells callers that any of the listed types may occur, so each checked type on the list needs a handler somewhere up the chain. It does not guarantee that any particular one will occur on a given call, and it does not require the method to throw all of them. The declaration describes possibility, not certainty.

Preconditions and Throwing Instead of Continuing

A precondition is a condition that must hold before a subroutine can do its job correctly. The root routine has two. First, the coefficient A must not be zero. Second, the discriminant B*B - 4*A*C must not be negative. Both conditions matter because the routine divides by 2*A and takes the square root of the discriminant, and neither operation is meaningful when its precondition fails. When a precondition is violated, the routine has to respond somehow. It could return a made-up value or keep computing with bad data, but either choice would hide the problem. Throwing an exception is often a more reasonable response to an illegal condition, because it stops the bad computation and makes the failure visible.

java
Notes on the code
  • The header matches the source example: a double-returning root method with three double parameters and a throws clause naming IllegalArgumentException
  • The first check enforces the precondition on A
  • The second check enforces the non-negative discriminant precondition before the square root is taken
Worked example

Tracing root with a negative discriminant

What happens when root(1, 0, 1) is called?

  1. 1
    Check the first precondition

    A is 1, which is not zero, so the first throw statement is skipped and execution continues.

  2. 2
    Compute the discriminant

    d = 0 * 0 - 4 * 1 * 1 = -4.

  3. 3
    Check the second precondition

    d is less than zero, so the condition is true and the second throw statement runs.

  4. 4
    Leave the routine

    The throw stops the normal path. The square root and division are never reached, and control moves to the caller.

  5. 5
    Caller decides

    If the caller has a catch clause for IllegalArgumentException, it takes over. Otherwise the program stops.

Result

An IllegalArgumentException with the message negative discriminant is thrown, and no value is returned.

Call rootA is zero?ThrowIllegalArgumentExceptionDiscriminantnegative?Return root valuetestyesnoyesno
When a precondition fails, how does the subroutine's execution path change compared with the normal path where it continues to completion?

Mistakes Beginners Make with throws

  • Common mistake

    Believing a throws clause makes the method throw the exception

    What it looks like
    static double root(double a, double b, double c) throws IllegalArgumentException with valid inputs
    Why it fails

    A throws clause only announces a possibility. The source states that it does not throw anything by itself.

    Do this instead

    Look for a throw statement in the body. The exception occurs only when that statement runs.

  • Common mistake

    Assuming that an unchecked exception must be declared or caught

    What it looks like

    Ignoring a possible IllegalArgumentException in a call to root

    Why it fails

    Exceptions such as IllegalArgumentException and NullPointerException are not mandatory. Catching or ignoring them is the programmer's choice.

    Do this instead

    Declare unchecked exceptions for documentation if useful, but do not expect the compiler to enforce them.

  • Common mistake

    Writing a throw of a checked exception with no handler and no throws clause

    What it looks like

    A method that throws IOException but has a header with no throws clause

    Why it fails

    The compiler requires checked exceptions to be handled, either by a try statement with a catch clause or by a throws clause in the header.

    Do this instead

    Add a throws clause to the header, or wrap the statement in a try statement with a matching catch clause.

  • Common mistake

    Thinking the throws clause is about how serious a problem is

    Why it fails

    The category depends on where the exception class sits in the hierarchy under Error and RuntimeException, not on severity.

    Do this instead

    Check the class's ancestry. Anything outside the Error and RuntimeException branches is checked.

Practice Reading Headers

MEDIUM

A method has the header static int parse(String s) throws NumberFormatException, IOException. Which exception class must a caller handle or declare, and why? Explain your answer using the checked and unchecked distinction.

Hints
  • Find each class in the hierarchy: is its ancestry through Error or RuntimeException?
  • NumberFormatException is a subclass of RuntimeException in the standard library, while IOException is not
  • A checked exception needs a handler or a matching throws clause up the chain

Key Takeaways

  • A checked exception is one whose class is not a subclass of Error or RuntimeException, and the compiler requires the program to handle it.
  • A checked exception can be handled inside a method with a try statement and a catch clause, or announced with a throws clause that passes the obligation to callers.
  • A throws clause only announces possible failure. The exception is actually thrown by a throw statement in the body, often when a precondition is violated.
  • When a declared exception is thrown, control leaves the subroutine at once, and the program stops if no caller has a matching catch clause.
  • A throws clause may list several comma-separated exception types, which promises that any of them might occur but guarantees none of them will.