Dev Learning Lab

The labs on this site need JavaScript to run. The same content is written out below.

Learn › Java › Exceptions

Checked and unchecked exceptions

Why do some exceptions keep the code from even compiling unless you handle them?

Java exceptions fall into two groups. With a checked exception (such as IOException), the compiler refuses to compile the code unless you catch the exception (try-catch) or declare that it is thrown (throws). An unchecked exception (typically RuntimeException or one of its subclasses) has no such requirement, so the code compiles with no handling at all.

Checked against real execution · All 24 combinations were actually compiled and run on JDK 21 and checked against the results

Concepts in this lab

Checked and unchecked exceptions
Unchecked exceptions are RuntimeException and its subclasses (in the specification, the Error classes are also unchecked), and the compiler does not check whether they are handled (examples are NullPointerException and IllegalArgumentException). Checked exceptions are all other Exception classes, and the code compiles only if they are handled with catch or declared with throws (examples are IOException and SQLException). By default, Spring's @Transactional rolls back only on RuntimeException and Error, and commits on checked exceptions.
Exception and Error
Both are subtypes of Throwable that represent abnormal situations while a program runs. An Exception is a situation that the application can catch and handle (such as invalid input or a missing file). An Error is a serious problem at the JVM level, such as running out of memory or a stack overflow, and applications usually do not try to recover from it.
throws clause
This is the syntax, written at the end of a method declaration as in throws IOException, that announces that the method can throw that exception to its caller. You must write it if you do not catch a checked exception inside the method, and the code then compiles only if the caller of that method in turn catches the exception or declares it with throws. It does not handle the exception. It passes the obligation to handle it on to the caller. For an unchecked exception you do not have to write it, and even if you do, it creates no obligation for the caller. It is a different word from throw, the statement that actually throws an exception.
Wrapped exception
This means putting an exception that was caught inside an exception of another kind and throwing that one. If you catch a checked exception and throw it again as an unchecked exception, as in throw new RuntimeException(e), the code compiles without a throws clause on every method that the exception passes through. When you pass the original exception to the constructor, it is kept as the cause and appears in the error output under Caused by. If you do not pass the original exception, there is no way to tell what went wrong in the first place, so always pass it. Wrapping does not make the exception go away, so if nothing catches it anywhere, execution stops.

Try these

Running with the default settings

  1. The compiler checks the read method: It contains throw new IOException(...). IOException is a checked exception. A line that can throw a checked exception compiles only if it is enclosed in a try-catch or the declaration of its method has a throws clause.
  2. read has neither a try-catch nor a throws clause: This is a compile-time error. The compiler reports "unreported exception IOException; must be caught or declared to be thrown". In other words, the code can throw an IOException without saying so, and the exception must be either caught or declared as thrown.
  3. Compilation failed: No class file is produced, so the program cannot run. A problem that would occur at run time was stopped in advance, before the program ran.

Result of this run

Code

import java.io.IOException;

public class Main {
    static String read(boolean missing) {
        if (missing) throw new IOException("file not found");
        return "content";
    }

    public static void main(String[] args) {
        String s = read(true);
        System.out.println("read: " + s);
        System.out.println("end of main");
    }
}

Assumptions of this model

All 24 combinations were actually compiled on JDK 21 on this PC from the same source as the code on the screen, and those that compiled were run, and the compilation result, the line with the error, the compiler message, the output, the exit status, and the first line of standard error were checked against the results. The compiler messages are written in the form that the command-line javac shows (class names only). It was confirmed in the same way that throwing an Error with no handling also compiles and that throwing Exception itself is treated as a checked exception. The model covers only the case with two methods (read and main). The rule is the same when the calls go deeper: every method that a checked exception passes through must catch it or declare it. An uncaught exception ending the program applies to the case where main is the only thread.

Previous lab: Exceptions and finally · Next lab: Generics

Concepts in this lab

What this lab shows

Settings

Code


      
    

Diagram

Run comparison

Try these