Checked vs Unchecked Exceptions in Java (With Hierarchy)

Checked vs unchecked exceptions in Java explained with the Throwable hierarchy, compiler rules, Java 25 examples and when to choose each kind.

Java exception hierarchy with Throwable, Error, Exception and RuntimeException marked as checked or unchecked

A checked exception is an exception that the compiler forces us to either catch or declare with throws, whereas an unchecked exception, which is a RuntimeException, an Error or one of their subclasses, compiles without either. Both kinds are thrown at run time, so the difference between checked and unchecked exceptions in Java is only what the compiler checks before the code runs.

We use checked exceptions for failures outside our code that a caller can react to, such as a missing file or a dropped network connection. Unchecked exceptions report bugs and broken assumptions, such as a null argument or a bad index, and errors report JVM problems such as running out of memory.

The following example triggers one exception of each kind while importing a playlist.

String error = "none";
try {
    String text = Files.readString(Path.of("missing.m3u"));  // checked: needs try or throws
} catch (IOException e) {
    error = e.getClass().getSimpleName();
}
String caught = error;                                 // "NoSuchFileException"
int seconds = Integer.parseInt("3:45");                // NumberFormatException, compiles without try

Notice that the compiler refuses the readString() call without the try block, while the parseInt() call compiles and fails only when it runs. We start from the exception class hierarchy, look at each kind with its compiler rules, compare them, and finish with a map of our guides for specific exceptions.

1. The Exception Hierarchy in Java

Every exception in Java is an object of a class that extends Throwable. The class tree under Throwable decides whether an exception is checked, and the rule in JLS 11.1.1 is short. RuntimeException, Error and their subclasses are unchecked, and every other subclass of Throwable is checked.

Java exception hierarchy with Throwable, Error, Exception and RuntimeException marked as checked or unchecked
Only RuntimeException, Error and their subclasses are unchecked; everything else under Throwable is checked

The four classes at the top of the tree carry different meanings, so it helps to know what each one stands for before we write a catch block.

ClassKindWhat it reportsExamples
ThrowableCheckedThe root type; we never throw or catch it in app codeException, Error
ExceptionCheckedConditions an app can expect and handleIOException, SQLException, InterruptedException
RuntimeExceptionUncheckedProgramming errors and invalid arguments or stateNullPointerException, IllegalArgumentException, ArithmeticException
ErrorUncheckedSerious JVM problems that an app does not try to fixOutOfMemoryError, StackOverflowError, NoClassDefFoundError

A common surprise is that RuntimeException extends Exception, so a catch (Exception e) block also catches every runtime exception. The JLS still exempts RuntimeException and its subclasses from the compiler check, because forcing try blocks around every array access or division would add code without making programs more correct.

2. Checked Exceptions and the Catch-or-Declare Rule

A checked exception describes a failure that can happen even when our code is correct, because it depends on something outside the program. Files get deleted, servers go down and users upload broken data. The compiler wants every caller to make a decision about such a failure, so a method that can throw a checked exception must either catch it or list it in its throws clause.

Say a music app lets users import playlists from .m3u files. The file may be gone, unreadable or on a disconnected drive, and the app should show a message instead of crashing. That is the kind of failure checked exceptions were designed for.

The following example is a method that reads the playlist file. The method Files.readString() declares throws IOException, and our method neither catches nor declares it.

// does not compile: unreported exception IOException
static String loadPlaylist(Path file) {
    return Files.readString(file);
}

The compiler stops with this message on JDK 25.

Playlist.java:4: error: unreported exception IOException; must be caught or declared to be thrown
        return Files.readString(file);
                               ^
1 error

2.1. Handling a Checked Exception With try-catch

We catch the exception when the current method knows what to do about it. A method that returns an empty playlist for a missing file can handle the failure itself, so the caller never sees the exception.

static List<String> loadOrEmpty(Path file) {
    try {
        return Files.readAllLines(file);
    } catch (IOException e) {
        return List.of();                       // missing or unreadable file
    }
}
List<String> tracks = loadOrEmpty(Path.of("missing.m3u"));   // []

The try-catch-finally guide covers multiple catch blocks, finally and the order in which they run.

2.2. Declaring a Checked Exception With throws

We declare the exception when only the caller can decide. A low-level reader has no idea whether the UI should retry, ask for another file or give up, so it passes the exception on with throws.

static List<String> loadTracks(Path file) throws IOException {
    return Files.readAllLines(file);
}
List<String> loaded = loadTracks(Path.of("missing.m3u"));   // NoSuchFileException

Every caller of loadTracks() faces the same rule again, until some method catches the exception. The difference between the two keywords is the topic of throw vs throws in Java.

The compiler checks only that a checked exception is handled or declared; it never checks that the handling makes sense. An empty catch block satisfies the compiler and hides the failure, so we log, rethrow or return a clear fallback value instead.

Most checked exceptions we meet in the JDK belong to a few families.

  • IOException and its subclasses, such as FileNotFoundException, NoSuchFileException and SocketTimeoutException.
  • SQLException from JDBC calls.
  • InterruptedException from blocking calls such as Thread.sleep() and BlockingQueue.take(), explained in our InterruptedException guide.
  • ReflectiveOperationException subclasses, such as ClassNotFoundException and NoSuchMethodException.
  • GeneralSecurityException subclasses, such as InvalidKeyException and NoSuchAlgorithmException.

3. Unchecked Exceptions

An unchecked exception most often means the code itself is wrong. A method received a null it did not expect, an index went past the end of a list, or an object was used in the wrong state. The compiler does not force us to catch or declare these exceptions, because the fix is to correct the code, not to wrap every line in a try block.

The following example shows three runtime exceptions that compile without any handling.

List<String> queue = List.of("intro", "chorus");
String third = queue.get(2);                          // IndexOutOfBoundsException
String title = null;
int length = title.length();                          // NullPointerException
Object track = "intro";
Integer id = (Integer) track;                         // ClassCastException

Since Java 15, helpful NullPointerException messages are on by default and name the call or variable that was null, which makes the bug in the second line quick to find. The NullPointerException guide covers ways to avoid it.

3.1. Throwing Unchecked Exceptions for Invalid Input

We also throw unchecked exceptions ourselves when a caller breaks a method contract. The JDK has standard classes for this purpose, so a method fails fast with a clear message instead of storing bad data. For example, a playlist must not accept a track with a negative duration.

record Track(String title, int seconds) {
    Track {
        Objects.requireNonNull(title, "title");
        if (seconds < 0) {
            throw new IllegalArgumentException("seconds must be >= 0: " + seconds);
        }
    }
}
Track ok = new Track("intro", 225);           // Track[title=intro, seconds=225]
Track bad = new Track("outro", -5);           // IllegalArgumentException: seconds must be >= 0: -5

The IllegalArgumentException article lists when to prefer it over IllegalStateException and NullPointerException.

3.2. Errors

Errors are unchecked too, but they come from the JVM, not from our logic. An OutOfMemoryError means the heap is full, and a StackOverflowError means a recursion went too deep. App code rarely catches them, because the JVM may be in a state where no recovery is reliable. Linkage errors, such as NoSuchMethodError, point to a mismatch between the classes we compiled against and the ones on the runtime classpath.

4. Checked vs Unchecked Exceptions Compared

The two kinds differ in what the compiler enforces and in what they tell the reader of the code. The table sums up the differences that matter when we write or call a method.

AspectChecked exceptionsUnchecked exceptions
ClassesThrowable and its subclasses, except the unchecked onesRuntimeException, Error and their subclasses
Compiler ruleMust be caught or declared with throwsNo rule; catching or declaring is optional
When thrownAt run timeAt run time
Typical causeConditions outside the program, such as files, network, databasesBugs, invalid arguments or state, JVM problems
Caller can recover?Often, by retrying or using a fallbackRarely; the code needs a fix
ExamplesIOException, SQLException, InterruptedExceptionNullPointerException, IllegalArgumentException, OutOfMemoryError
In lambdasNot allowed by Function, Supplier and the other standard interfacesAllowed anywhere

The last row causes daily friction. The functional interfaces in java.util.function declare no checked exceptions, so a lambda that calls Files.readString() inside map() does not compile. The common fixes are wrapping the exception or extracting a helper method, as shown in our guide on handling checked exceptions in streams.

5. Choosing Between Checked and Unchecked for Our Own Exceptions

When we create our own exception class, we pick the kind by choosing the superclass. The guideline from the Oracle Java Tutorials still holds. If a client can reasonably be expected to recover from an exception, we make it a checked exception; if a client cannot do anything to recover, we make it unchecked.

In practice, many teams and frameworks lean toward unchecked exceptions. Spring translates SQLException into unchecked DataAccessException subclasses, and the JDK added UncheckedIOException in Java 8 so that stream code can carry an IOException. Checked exceptions cost a throws clause on every method between the thrower and the handler, which is worth it only when callers do something useful with the failure.

The following example wraps a checked exception into an unchecked one at the boundary of a stream pipeline. The original exception stays attached as the cause, so the stack trace keeps the real reason.

static String readQuietly(Path file) {
    try {
        return Files.readString(file);
    } catch (IOException e) {
        throw new UncheckedIOException("Cannot read " + file, e);
    }
}
List<String> names = List.of("a.m3u", "b.m3u");
List<String> files = names.stream().map(Path::of).map(file -> readQuietly(file)).toList();   // UncheckedIOException: Cannot read a.m3u

Our post on custom exceptions in Java shows how to design a small hierarchy of app exceptions, and the exception handling best practices article collects the general rules.

6. Synchronous and Asynchronous Exceptions

Checked and unchecked describe what the compiler enforces. A second, rarely needed split describes where an exception can appear. Most exceptions are synchronous, which means the thread causes them itself at a point in the code that can throw, such as a division, a method call or a throw statement.

An asynchronous exception can appear at any point in the program. In Java 25, the JLS lists only one source, which is an internal error or resource limit inside the JVM, and the exception is a VirtualMachineError such as OutOfMemoryError or StackOverflowError. Those two errors can also be synchronous, for example when a large array allocation fails at a known line.

Older tutorials name Thread.stop() as a source of asynchronous exceptions. Since Java 20, Thread.stop() throws UnsupportedOperationException instead of stopping the thread, and ThreadGroup.stop() no longer exists in Java 25.

Thread worker = new Thread(() -> { });
worker.stop();                                     // UnsupportedOperationException

Pressing Ctrl+C is not an exception either. The JVM receives a signal from the operating system and starts its shutdown sequence, which runs shutdown hooks registered with Runtime.addShutdownHook(); no catch block sees it. To stop a thread cooperatively, we call interrupt() and let the thread check its interrupted flag.

7. Exception Guides Grouped by Task

Once the checked and unchecked rules are clear, most questions are about one specific exception or error message. Our guides cover them by task, and each one starts with the cause and the fix.

TaskGuides
Write handling codeSuppressed exceptions and try-with-resources, try-with-resources statement
Fix common runtime exceptionsIllegalStateException: No match found, UnsupportedOperationException on lists, ArrayStoreException, IllegalMonitorStateException
Handle exceptions in threadsUncaughtExceptionHandler
Fix class version and linkage errorsClass file has wrong version 61.0, Unsupported class file major version, IncompatibleClassChangeError: Implementing class
Fix build path and dependency errorsHttpServlet not found on the build path, ClassNotFoundException: FileItemFactory, Taglib definition not consistent with specification version
Fix test and benchmark toolingJMH Unable to find the resource /META-INF/BenchmarkList, This version of ChromeDriver only supports Chrome version
Fix security and TLS exceptionsInvalidKeyException: Parameters missing, SSLHandshakeException

For the language basics behind these posts, such as classes, methods and control flow, the Java tutorial lists every topic in reading order.

8. Checked and Unchecked Exception FAQs

Most classification questions name one exception class, so each answer gives the verdict first, followed by the superclass that decides it.

8.1. Is IOException checked or unchecked?

Checked. IOException extends Exception and not RuntimeException, so every method that can throw it must catch it or declare it. Its unchecked companion for lambdas and streams is UncheckedIOException.

8.2. Is NullPointerException checked or unchecked?

Unchecked. NullPointerException extends RuntimeException, so the compiler never asks us to handle it. It points to a bug, and the fix is a null check, Objects.requireNonNull() or Optional, not a catch block.

8.3. Are checked exceptions thrown at compile time?

No. Every exception is thrown while the program runs. The compiler only verifies that checked exceptions are caught or declared, which is why some books call them compile-time checked exceptions.

8.4. Can we catch an Error in Java?

Yes, the syntax allows it, because Error is a Throwable. App code catches errors only in narrow cases, such as a top-level handler that logs a StackOverflowError from a recursive parser before failing the request.

8.5. Why can we catch Exception when the try block throws nothing?

The compiler rejects a catch for a checked exception that the try block cannot throw, such as catch (IOException e) around plain arithmetic. JLS 11.2.3 makes an exception for Exception and Throwable, because they also catch unchecked exceptions that any code can throw.

Order.java:10: error: exception IOException is never thrown in body of corresponding try statement

8.6. Is SQLException checked?

Yes. SQLException is a checked exception, so plain JDBC code needs try blocks or throws clauses. Frameworks such as Spring JDBC and JPA providers wrap it in unchecked exceptions.

9. Conclusion

Checked exceptions are Throwable subclasses outside RuntimeException and Error, and the compiler makes us catch or declare them. They fit failures that callers can handle, such as missing files and network errors.

Unchecked exceptions report bugs, invalid input and JVM problems. We fix their cause instead of catching them, throw standard ones such as IllegalArgumentException for broken contracts, and wrap checked exceptions in unchecked ones where a throws clause does not fit, such as inside lambdas.

10. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. As i heard that checked exceptions should not consider at all. Most languages will not use them too. But one can always throw a subclass of Runtime Exception i.e, unchecked exception according to my thoughts checked exceptions are useful as they are used when someone wants to force the user of their API to think how to carry on the exceptional situation. And people hate checked exceptions because they are overused in the Java platform.

    The main difference with Checked and UnChecked Exception is that checked Exception requires mandatory try catch or try finally block but unchecked Exception don’t. Another difference between Checked and UnChecked Exception is in where to use them.

    Checked Exception should be used if you know how to recover from Exception while Unchecked Exception should be used for programming errors.

    And i have a question for you regarding this article, can you please clarify me. My question is that If you can handle checked exception, then why not each exception is checked exception? why java have a concept of unchecked exception? Thanks for your detailed blog…

    • Because an unchecked exception cannot be detected at compilation time e.g if u try to access an index of array beyond range it can only be detected only when the program runs not before that.

  2. The exceptions which are checked by compiler for smooth execution of the program at run time are called checked exception. keep blogging.

  3. Hi Lokesh,

    Checked exceptions are the subclass of Exception and Unchecked exception are the subclass of RuntimeException(RuntimeException is a subclass of Exception again).

    Suppose you have two custom exception classes MyException1.java(This extends RuntimeException) and MyException2.java(Extends Exception class).

    Suppose a method in any class throws MyException1 exception. While calling this method in any other places,it is not mandatory to catch the exception.But if the method throws MyException2 exception you are forced to catch the exception.(Either surround with try/catch or throws the exception).

    My question is when your custom exception class extends RuntimeException why it is not treated as a checked exception,? even tough RuntimeException the Exception class?

    Regards
    Partha

      • I already mentioned: “Here, the strange thing is that RuntimeException is itself subclass of Exception i.e. all Unchecked exception classes should have been checked exceptions implicitly, BUT they are not. I am confuse. Any thoughts??”

        I am really clueless about this design choice, JDK designers followed…

        • As per my thought, it is just an indication to the JVM, so it works accordingly. from the Java DOC, RuntimeException is Unchecked and Exception is Checked. So when our exception class extends the RuntimeException , it will be treated as Unchecked exception by the JVM and Exception will be treated as Checked exception.

          Not sure about this but this is my thought.

  4. Hi,
    Would like to know how compiler comes to know that a particular piece of code would result into a CheckedException. Meaning, if we use FILE IO or DB connection related utilities and don’t declare or handle them, then its a compile time error. In a way we can say that compiler is monitoring each and every line of code that we are introducing into the application but I am not sure how it understands and forces us to handle CheckedExceptions. Please suggest.

  5. Nice explanation. But I think Checked exceptions are a thing of past and do not have much significance as I have written the reasons here

  6. Hello Lokesh. Thanks for this post and very well explained. could you please tell me the difference between
    Throws and Throw ? as i am little but confusing about these two

    • `throws` declares the exceptions the method throws, and is used in the method signature.

      `throw` is used to throw an exception

      “`
      void foo() throws Exception1 {
      …
      throw new Exception1(“Something bad happened”);
      …
      }
      “`

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.