throw vs throws in Java: Differences and Examples

throw vs throws in Java compared with a table and examples, plus exception propagation, rethrowing and the throws rules for overriding methods.

Diagram of a method signature with a throws clause, a throw statement in the body and the exception travelling to the caller's catch block

In Java, the throw keyword raises one exception object at a specific point in a method body, whereas the throws keyword in a method signature declares which exceptions the method may pass on to its callers. One is an action that happens at run time, and the other is a declaration that the compiler reads.

We use throw to reject bad input or an illegal state as soon as we detect it. We use throws to tell callers about checked exceptions, such as IOException, that the method does not handle itself.

The following example loads a library catalog file and uses both keywords.

// throws: IOException may reach the caller
static String loadCatalog(Path file) throws IOException {
    if (!file.toString().endsWith(".csv")) {
        throw new IllegalArgumentException("not a CSV: " + file);   // throw: raised here
    }
    return Files.readString(file);
}
String wrongType = loadCatalog(Path.of("books.txt"));    // IllegalArgumentException: not a CSV: books.txt
String missing = loadCatalog(Path.of("books.csv"));      // NoSuchFileException: books.csv

The keyword throws names a class, IOException, while throw takes an object created with new. We look at each keyword, compare them in a table, follow an exception up the call stack, and cover rethrowing, overriding rules and throw inside expressions.

1. The throw Statement

A throw statement takes an expression whose type is Throwable or a subclass, and it ends the normal flow of the method at that line. The JVM looks for a matching catch block in the current method, and if there is none, in each caller up the stack. Statements after a throw in the same block are unreachable, so the compiler rejects them.

Say a library app lends books. Lending a book that is already on loan is a bug in the calling code or a race between two librarians, and the method should refuse loudly instead of creating a second loan record.

class Library {
    private final Set<String> onLoan = new HashSet<>(Set.of("dune"));

    String lend(String bookId) {
        Objects.requireNonNull(bookId, "bookId");
        if (onLoan.contains(bookId)) {
            throw new IllegalStateException("already on loan: " + bookId);
        }
        onLoan.add(bookId);
        return "lent " + bookId;
    }
}
Library library = new Library();
String loan = library.lend("emma");        // "lent emma"
String twice = library.lend("dune");       // IllegalStateException: already on loan: dune

Since IllegalStateException is unchecked, the method needs no throws clause. A throw statement works the same way for checked exceptions, but the compiler wants a catch block or a throws clause, as we see in section 2.

Two details about the operand matter in practice. The operand can be any expression of a Throwable type, so we can throw an exception object that we created earlier or caught. If the expression evaluates to null, the JVM throws a NullPointerException instead.

RuntimeException notSet = null;
String thrown;
try {
    throw notSet;
} catch (NullPointerException e) {
    thrown = e.getClass().getSimpleName();
}
String result = thrown;                      // "NullPointerException"

2. The throws Clause

A throws clause sits after the parameter list of a method or a constructor and lists one or more exception classes, separated by commas. It is part of the method’s contract, so every caller sees which failures it has to plan for.

For a checked exception, the throws clause is the alternative to catching it, and the compiler insists on one of the two. The following method calls Files.readAllLines(), which can throw IOException, and catches nothing.

// does not compile: unreported exception IOException; must be caught or declared to be thrown
static List<String> readMembers(Path file) {
    return Files.readAllLines(file);
}

Adding the clause moves the decision to the caller.

static List<String> readMembers(Path file) throws IOException {
    return Files.readAllLines(file);
}
List<String> members = readMembers(Path.of("members.csv"));   // NoSuchFileException: members.csv

Declaring unchecked exceptions such as IllegalArgumentException in throws is legal but changes nothing for the compiler. We document them with a Javadoc @throws tag instead. The full rules for both kinds are in checked vs unchecked exceptions.

Constructors can have a throws clause too, which is how FileReader(String) declares FileNotFoundException. A failing constructor never returns an object, so the caller has nothing to clean up.

3. throw vs throws in Java

The two keywords meet in most methods that deal with checked exceptions, but they answer different questions. The keyword throw answers what goes wrong at this line, and throws answers what can go wrong when someone calls the method.

Diagram of a method signature with a throws clause, a throw statement in the body and the exception travelling to the caller's catch block
throws declares the exception in the signature; throw raises it in the body, and the caller catches it
Aspectthrowthrows
What it isA statementA clause in a method or constructor declaration
Where it goesInside a method body, constructor, initializer or lambdaAfter the parameter list
Followed byOne Throwable object, e.g. new IOException(“…”)One or more class names, e.g. IOException, SQLException
How manyOne exception per statementAny number of exception types
When it actsAt run time, when the line executesAt compile time, when the compiler checks callers
Needed for checked exceptionsRaises themDeclares them, unless the method catches them
Needed for unchecked exceptionsRaises themOptional, for documentation only

4. How a Thrown Exception Travels to the Caller

After a throw, the JVM unwinds the call stack one method at a time until it finds a catch block whose type matches. Each method it leaves on the way runs its finally blocks. Checked and unchecked exceptions propagate in the same way; the only difference is that the compiler makes every method on the path either catch or declare a checked exception.

The following example has three layers of a library app. The repository throws, the service passes the exception on, and the controller catches it and returns a message for the user.

static String findTitle(String bookId) throws IOException {
    throw new FileNotFoundException("catalog.csv");
}

static String describeBook(String bookId) throws IOException {
    return "Title: " + findTitle(bookId);
}

static String showBook(String bookId) {
    try {
        return describeBook(bookId);
    } catch (IOException e) {
        return "Catalog unavailable: " + e.getMessage();
    }
}
String page = showBook("emma");    // "Catalog unavailable: catalog.csv"

If showBook() did not catch the exception, it would reach the thread’s uncaught exception handler, which prints the stack trace and ends the thread. The try-catch-finally guide shows the order of catch and finally blocks along the way.

5. Rethrowing and Wrapping Exceptions

A catch block can use throw again. We rethrow the same object when we only want to log the failure and let a higher layer decide, and we wrap it in a new exception when the caller should not depend on low-level classes. Wrapping passes the original as the cause, so the stack trace shows both.

static String titleOrFail(String bookId) {
    try {
        return findTitle(bookId);
    } catch (IOException e) {
        throw new IllegalStateException("Cannot load book " + bookId, e);
    }
}
String wrapped = titleOrFail("emma");   // IllegalStateException: Cannot load book emma

5.1. Precise Rethrow

Since Java 7, the compiler tracks which checked exceptions a try block can throw. If a catch (Exception e) block rethrows e without assigning it, the method only needs to declare the checked exceptions the try block can throw, not Exception.

static String auditedTitle(String bookId) throws IOException {
    try {
        return findTitle(bookId);
    } catch (Exception e) {
        IO.println("lookup failed: " + e.getMessage());
        throw e;                                  // still only IOException
    }
}
String audited = auditedTitle("emma");   // FileNotFoundException: catalog.csv

Our guide to custom exceptions in Java shows how to design the wrapper classes, and suppressed exceptions covers the case where two exceptions happen at once and neither should be lost.

6. throws Clauses in Overriding Methods

An overriding method may declare fewer or narrower checked exceptions than the method it overrides, but never new or broader ones. Code written against the parent type must stay correct, and it only handles the exceptions the parent declared. Unchecked exceptions are free from this rule.

// does not compile: overridden method does not throw IOException
class CatalogSource {
    String read() throws FileNotFoundException { return ""; }
}
class RemoteSource extends CatalogSource {
    @Override
    String read() throws IOException { return ""; }
}

The overriding method can narrow the clause or drop it entirely.

class FileSource {
    String read() throws IOException { return "file"; }
}
class MemorySource extends FileSource {
    @Override
    String read() { return "memory"; }            // no throws clause needed
}
String fromMemory = new MemorySource().read();     // "memory"

7. throw Inside Expressions and Lambdas

A throw is a statement, but a switch expression arm accepts it, and a lambda accepts it only inside a block body, such as id -> { throw new IllegalStateException(); }. The arm default -> throw … is the standard way to reject a value that should not occur.

static int loanDays(String format) {
    return switch (format) {
        case "hardcover" -> 21;
        case "magazine" -> 7;
        default -> throw new IllegalArgumentException("unknown format: " + format);
    };
}
int magazine = loanDays("magazine");          // 7
int ebook = loanDays("ebook");                // IllegalArgumentException: unknown format: ebook

Lambdas for Function, Supplier and the other standard functional interfaces may throw unchecked exceptions only, because the interface methods have no throws clause. The guide on exceptions in streams shows how to handle checked exceptions there.

8. throw and throws FAQs

Most of these questions test one rule, the compile-time check for checked exceptions.

8.1. Can we throw multiple exceptions at once?

No. A throw statement raises a single object. To report several failures, we throw one exception and attach the others with addSuppressed(), or collect them in a list inside a custom exception.

8.2. Can we use throw without throws?

Yes, for unchecked exceptions, and for checked exceptions that a surrounding try block catches. A checked exception that leaves the method needs throws.

8.3. Can the main method declare throws Exception?

Yes. The declaration static void main(String[] args) throws Exception compiles, and an exception that leaves main() reaches the default handler, which prints the stack trace. It is fine for small tools and demos, while real apps handle errors and return a clear exit code.

8.4. Can a constructor use throw and throws?

Yes. Constructors validate their arguments with throw and declare checked exceptions with throws, such as FileInputStream(File) throws FileNotFoundException.

8.5. Is the throws clause checked at run time?

No. The JVM does not verify throws clauses; they exist for the compiler and for readers. Bytecode compiled from other JVM languages, such as Kotlin, can throw checked exceptions that no throws clause declares.

9. Conclusion

The keyword throw raises one exception object at the line where we detect a problem, and it works for checked and unchecked exceptions alike. The keyword throws declares checked exceptions in a method or constructor signature, so callers either catch them or declare them in turn.

Both kinds of exceptions travel up the call stack until a catch block matches. When we rethrow, we pass the original exception as the cause, and when we override a method, we keep its throws clause the same or narrower.

10. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. This statement is wrong
    “For example, FileNotFoundException is an unchecked exception.” Can you please correct it

  2. “A checked exception is propagated to the caller method, while unchecked exceptions are not propagated thus may not require explicit exception handling.”

    This is quite not correct.

    The unchecked exception in fact does propagate through the method call stack and is being handled whenever it finds an explicit handler or propagates to the JVM and is being handled by the default JVM exception handler.

    Checked exceptions do not propagate. And as a result, this has to be explicitly mentioned/declared to the caller method in the call stack using throws keyword. It is a declaration to the caller method to provide a handler or propagate it to the next caller in the stack using the throws keyword. Even if the exception needs to be propagated to the JVM to be handled by default handler, you have to explicitly define/declare the Exception being thrown.

    Both unchecked and checked exception does occur at runtime. If you compile code with the lines :
    FileInputStream inputStream = new FIS(new File(“{filename}”);

    The compiler only checks if you have handled the Exception/declared the exception. There is no way compiler knows if the file exists/not and there will be an IOException.

    I think I reason behind the names checked and unchecked exception comes from the way compiler checks if you have declared all your exceptions which do not propagate (IOException/SQLException) and don’t check the ones which automatically propagate(RuntimeException). It is designed in a way where the compiler can pre-check the fact that checked exceptions can propagate.

    Please let me know what you think.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.