BeginnerReading time: ~12 min

Types

Checked vs unchecked exceptions, Error vs Exception and hierarchy

The first step in good exception handling is knowing what kind of failure you are looking at: a recoverable condition, a caller mistake, an infrastructure problem, or a JVM-level failure.

1. Definition

What is an exception type?

In Java, exception types describe abnormal situations that interrupt the normal control flow of a program.

They are represented by classes in the Throwable hierarchy.

That hierarchy is not just a taxonomy.

It encodes important design signals about:

  • whether callers are expected to recover
  • whether the compiler enforces handling
  • whether the failure is probably caused by business conditions, programmer mistakes, or system breakdown

Why does the type matter?

The concrete exception type affects:

  • method signatures
  • API ergonomics
  • logging strategy
  • retry behavior
  • transaction behavior
  • monitoring and alerting
  • interview answers about design quality

If you choose the wrong exception type, the code may still compile, but the API becomes misleading.

That is why senior engineers treat exception types as part of the public contract, not just as error labels.

The root of the hierarchy

All throwables inherit from Throwable.

The two main branches are:

  • Error
  • Exception

Inside Exception, the most important split is:

  • checked exceptions
  • unchecked exceptions (RuntimeException and its subclasses)

This split is the core of most Java exception-handling discussions.

2. Core Concepts

2.1 `Throwable`

2.1.1 Keywords and contracts you should state explicitly here

This topic becomes clearer if you deliberately name the hierarchy and contract words behind exception design:

  • Throwable — the common root type of everything that can be thrown.
  • Error — a JVM- or environment-level failure category that application code usually does not recover from as normal flow.
  • Exception — the application-facing branch for exceptional conditions.
  • checked exception — an exception the compiler forces you to catch or declare.
  • unchecked exception — a RuntimeException-based failure not enforced by method signatures.
  • cause — the original underlying failure linked to a higher-level exception.
  • stack trace — the recorded execution path at the point of failure.
  • suppressed exception — a secondary failure, often from cleanup, attached to a primary exception.

These are not just labels. They communicate recovery expectations, API burden, and failure semantics.

Throwable is the common supertype of everything that can be thrown.

It carries:

  • the message
  • the stack trace
  • the cause
  • suppressed exceptions

In practice, you rarely design APIs directly around Throwable.

You usually work at a more specific level.

2.2 `Error`

Error represents serious problems that applications usually should not try to handle as ordinary business flow.

Typical examples:

  • OutOfMemoryError
  • StackOverflowError
  • NoClassDefFoundError

These often indicate:

  • JVM/resource failure
  • deployment/configuration issue
  • corrupted runtime assumptions

A common interview point is that Error exists in the hierarchy but is generally not treated like normal application exceptions.

2.3 Checked exceptions

Checked exceptions are exceptions that the compiler forces you to either:

  • catch
  • or declare with throws

Examples:

  • IOException
  • SQLException in older APIs
  • ClassNotFoundException

Checked exceptions signal that the caller is expected to make a conscious decision.

That decision may be:

  • recover
  • translate
  • propagate with context

The design intent is explicitness.

2.4 Unchecked exceptions

Unchecked exceptions are subclasses of RuntimeException.

Examples:

  • IllegalArgumentException
  • IllegalStateException
  • NullPointerException
  • IndexOutOfBoundsException

These usually signal:

  • programming mistakes
  • violated preconditions
  • invalid state transitions
  • misuse of an API

They are not enforced in method signatures.

That makes APIs simpler, but also puts more responsibility on the developer to document behavior well.

2.5 Recovery versus bug signaling

One of the strongest practical heuristics is this:

  • use checked exceptions when the caller can reasonably recover
  • use unchecked exceptions when the issue is usually a bug or contract violation

This rule is not perfect, but it is much better than memorizing isolated examples.

2.6 Exception hierarchy as API communication

Exception types are a form of communication.

For example:

  • IllegalArgumentException says the caller passed a bad value
  • IllegalStateException says the object is in the wrong state
  • UnsupportedOperationException says the operation is intentionally unavailable
  • IOException says an external I/O boundary failed

Choosing the right type improves readability and debuggability.

2.7 Cause chains

A good exception model preserves the original cause.

throw new IllegalStateException("Failed to load user profile", ioException);

Without cause chaining, debugging becomes much harder.

The type tells you what happened at the current abstraction level.

The cause tells you where it came from.

3. Practical Usage

Distinguish domain problems from infrastructure problems

Suppose a payment fails.

The exception strategy should distinguish between:

  • invalid input from the caller
  • business rejection like insufficient credit
  • network timeout to a remote provider
  • internal programming bug

These are not interchangeable categories.

They imply different handling and observability.

Typical uses of checked exceptions

Checked exceptions fit best when:

  • the failure is external
  • the caller may retry or fallback
  • the failure is expected in normal operation
  • the method boundary is low-level or infrastructure-oriented

Examples include file access, reflection lookups, and some legacy APIs.

Typical uses of unchecked exceptions

Unchecked exceptions fit best when:

  • a precondition is violated
  • an object is used incorrectly
  • a bug is likely present
  • forcing throws everywhere would add noise without helping recovery

This is why most modern framework APIs prefer unchecked exceptions for programming errors.

Common design question

Should every business failure be a checked exception?

Not necessarily.

Many teams model expected business outcomes as:

  • return types
  • result objects
  • validation objects
  • domain-specific unchecked exceptions at boundaries

The right choice depends on how exceptional the failure really is.

Logging and alerting impact

Exception type also affects operational handling.

For example:

  • IllegalArgumentException from a public API may indicate bad client input
  • IllegalStateException may indicate an internal bug
  • repeated IOException may indicate infrastructure instability
  • OutOfMemoryError usually demands immediate operational investigation

This is where exception taxonomy becomes production engineering, not just syntax.

4. Code Examples

Example 1 — Checked exception

import java.io.IOException;

public class FileReaderService {
    public String readConfig() throws IOException {
        throw new IOException("Config file not found");
    }
}

The caller must explicitly decide how to handle this failure.

Example 2 — Unchecked exception for invalid arguments

public class AgeValidator {
    public void validate(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("age must be non-negative");
        }
    }
}

This communicates a caller contract violation.

Example 3 — Illegal state

public class ReportExporter {
    private boolean initialized;

    public void initialize() {
        initialized = true;
    }

    public void export() {
        if (!initialized) {
            throw new IllegalStateException("Exporter must be initialized first");
        }
    }
}

This signals misuse of object lifecycle rather than bad input value.

Example 4 — Preserve cause

import java.io.IOException;

public class ProfileService {
    public void loadProfile() {
        try {
            throw new IOException("Disk read failed");
        } catch (IOException exception) {
            throw new IllegalStateException("Could not load profile", exception);
        }
    }
}

The abstraction changes, but the original cause remains available.

Example 5 — `Error` is different

public class ErrorExample {
    public static void main(String[] args) {
        throw new StackOverflowError("Demonstration only");
    }
}

This is not how normal business errors should be modeled.

5. Trade-offs

Aspect Advantage Cost / risk
Checked exceptions Forces explicit handling Can clutter signatures and call chains
Unchecked exceptions Cleaner APIs Easier to ignore if documentation is weak
Specific exception types Better diagnostics and intent Requires better design discipline
Broad exception types Less initial effort Loses precision and encourages bad handling
Preserving causes Easier debugging Slightly more code, but usually worth it

The design tension is always the same.

Do you want stronger explicitness, or a lighter API surface?

Good design chooses based on caller responsibilities, not habit.

6. Common Mistakes

1. Throwing `Exception` everywhere

This destroys intent.

The code compiles, but the API no longer communicates what actually went wrong.

2. Using checked exceptions for programmer bugs

For example, invalid method arguments are usually better represented with unchecked exceptions such as IllegalArgumentException.

3. Swallowing exception causes

If you replace the exception type without preserving the cause, diagnosis becomes much harder.

4. Catching `Throwable`

This is almost always too broad.

It captures Error as well, which is usually not what you want in ordinary application logic.

5. Treating `Error` like business flow

OutOfMemoryError is not a validation result.

It is a system-level failure signal.

6. Using exception types inconsistently

If one part of the code uses IllegalArgumentException and another throws IllegalStateException for the same caller mistake, the API becomes confusing.

7. Deep Dive

Why Java kept checked exceptions

Checked exceptions are controversial.

Many developers love the explicitness.

Many others dislike the boilerplate.

Still, they express an important design idea:

some failures deserve first-class attention from the caller.

Whether you use them heavily or lightly, you should understand the intent behind them.

Exception types and architecture

At lower layers, infrastructure-specific checked exceptions can make sense.

At higher layers, applications often translate them into:

  • domain exceptions
  • service-layer exceptions
  • HTTP responses
  • error result objects

This is why exception types are part of architectural boundaries.

Interview framing

A strong interview answer does not stop at “checked exceptions must be declared.”

It usually adds:

  • what kind of failure the type represents
  • whether the caller can recover
  • whether the type is part of API communication
  • how the choice affects debugging and design

That extra layer is what makes the answer senior.

Precision over dogma

There is no perfect universal rule such as “checked is always better” or “unchecked is always modern.”

The better rule is:

choose the type that best communicates responsibility and likely handling.

Production impact

Exception type choices affect:

  • metrics dashboards
  • retry policies
  • transaction rollbacks
  • REST error mapping
  • support investigations

Once you operate systems in production, bad exception taxonomy becomes very visible.

8. Interview Questions

What is the difference between `Error` and `Exception`?

Error usually represents serious system-level problems that applications do not normally recover from, while Exception represents conditions application code may handle or propagate.

What is the difference between checked and unchecked exceptions?

Checked exceptions must be caught or declared, while unchecked exceptions do not have to appear in method signatures and usually represent programmer mistakes or contract violations.

When would you use `IllegalArgumentException`?

When the caller passes an invalid value to a method and the failure indicates a violated precondition.

Why is `IllegalStateException` different?

It signals that the object or system is in the wrong state for the requested operation, even if the immediate method arguments are valid.

Why should you preserve the cause?

Because the higher-level exception explains the failure at the current abstraction, while the cause preserves the lower-level technical reason for debugging.

9. Glossary

Term Meaning
Throwable Root type for everything that can be thrown
Error Serious system-level failure type
checked exception Exception that must be caught or declared
unchecked exception RuntimeException subtype not enforced in signatures
cause Original underlying throwable attached to another exception
contract violation Caller misuse such as invalid input or invalid state
propagation Letting the exception move to a higher layer
translation Converting a low-level exception into a more suitable abstraction

10. Cheatsheet

  • Throwable splits mainly into Error and Exception.
  • Checked exceptions force explicit handling or declaration.
  • Unchecked exceptions usually signal bugs, misuse, or violated contracts.
  • IllegalArgumentException is about bad input.
  • IllegalStateException is about bad object/system state.
  • Error is usually not business-flow handling territory.
  • Specific exception types communicate intent better than broad ones.
  • Preserve the cause when translating exceptions.
  • Choose types based on caller responsibility and recovery options.
  • In interviews, explain both semantics and design consequences.

🎮 Games

10 questions