By WebNest Studio Engineering Team
Java - Core Tutorial
Exception Handling Rules with Method Overriding
When a subclass overrides a method that declares checked exceptions, Java enforces a specific compatibility rule so that code calling through the superclass reference is never surprised by an exception it wasn't told to expect. This rule only concerns checked exceptions; unchecked exceptions are unrestricted.
Getting this rule wrong is a very common cause of "cannot override" compile errors, especially when a subclass tries to widen the exception contract instead of narrowing or preserving it.
Why it matters
Code that calls a method through a parent reference only prepares for the exceptions the parent declares. If a subclass could throw a new checked exception, that code would break without warning. The overriding rule prevents this, and it explains a compile error that looks strange until you know the reason.
The Core Rule
An overriding method may not declare a checked exception that is broader (higher up the exception hierarchy) than what the overridden method declared. Specifically, the overriding method is allowed to declare: the exact same checked exception, a subclass of that checked exception, no checked exception at all, or omit the throws clause entirely. It can never declare a new, broader, or unrelated checked exception that the superclass method did not already declare (or a superclass of it).
Unchecked Exceptions Are Unrestricted
This rule applies only to checked exceptions (those that extend Exception but not RuntimeException). An overriding method is completely free to throw any unchecked exception (RuntimeException and its subclasses) regardless of what the superclass method declares, because unchecked exceptions are never part of a method's compile-time contract in the first place.
Why the Rule Exists
Polymorphism means code can call a method through a superclass or interface reference without knowing the actual runtime subclass. If an overriding method could throw a broader checked exception, code written against the superclass type — which only prepared to catch the superclass's declared exceptions — could be blindsided by a checked exception it never accounted for. Restricting overrides to the same, narrower, or no checked exceptions preserves that contract.
Examples
Invalid override: broadening the checked exception fails to compile
import java.io.IOException;
class Reader1 {
void read() throws IOException {
System.out.println("Reading with possible IOException");
}
}
class BadReader extends Reader1 {
// COMPILE ERROR: Exception is broader than IOException declared by the superclass
@Override
void read() throws Exception {
System.out.println("Not allowed to widen the checked exception");
}
}
Compile-time error: "read() in BadReader cannot override read() in Reader1; overridden method does not throw java.lang.Exception"
Valid overrides: same, narrower (subclass), none, and any unchecked exception freely
import java.io.FileNotFoundException;
import java.io.IOException;
class Reader2 {
void read() throws IOException {
System.out.println("Base reader");
}
}
class SameException extends Reader2 {
@Override
void read() throws IOException { // same checked exception: allowed
System.out.println("Same exception type");
}
}
class NarrowerException extends Reader2 {
@Override
void read() throws FileNotFoundException { // subclass of IOException: allowed
System.out.println("Narrower checked exception");
}
}
class NoCheckedException extends Reader2 {
@Override
void read() { // no throws clause at all: allowed
System.out.println("No checked exception declared");
}
}
class UncheckedIsFree extends Reader2 {
@Override
void read() { // free to throw ANY unchecked exception, undeclared
System.out.println("About to throw an unchecked exception");
throw new IllegalStateException("Unchecked exceptions bypass this rule entirely");
}
}
public class OverrideExceptionDemo {
public static void main(String[] args) throws IOException {
Reader2[] readers = { new SameException(), new NarrowerException(), new NoCheckedException() };
for (Reader2 r : readers) {
r.read();
}
try {
new UncheckedIsFree().read();
} catch (IllegalStateException e) {
System.out.println("Caught: " + e.getMessage());
}
}
}
Same exception type
Narrower checked exception
No checked exception declared
About to throw an unchecked exception
Caught: Unchecked exceptions bypass this rule entirely
Common Mistakes
- Trying to declare a broader checked exception in an override (e.g. overriding a method that throws IOException with one that throws Exception) — this is always a compile error.
- Assuming this rule restricts unchecked exceptions too — RuntimeException subtypes can be thrown from an override with no declaration at all.
- Forgetting that declaring no throws clause in the override is always legal, even if the superclass method declared checked exceptions.
- Confusing "narrower exception" with "fewer exceptions" — declaring a subclass of the original exception type is what matters, not just declaring less text.
Exercise
This code does not compile: the override in FileSource declares Exception, which is broader than the IOException declared by the parent. Change the override so that it declares a narrower exception, and the program compiles.
Expected output: file read
import java.io.FileNotFoundException;
import java.io.IOException;
class Source {
void read() throws IOException {
System.out.println("source read");
}
}
class FileSource extends Source {
@Override
void read() throws Exception {
System.out.println("file read");
}
}
public class OverrideRule {
public static void main(String[] args) throws IOException {
Source source = new FileSource();
source.read();
}
}
Show hints
- An override may declare the same checked exception, a subclass of it, or none at all.
FileNotFoundExceptionis a subclass ofIOException.
Show solution
import java.io.FileNotFoundException;
import java.io.IOException;
class Source {
void read() throws IOException {
System.out.println("source read");
}
}
class FileSource extends Source {
@Override
void read() throws FileNotFoundException {
System.out.println("file read");
}
}
public class OverrideRule {
public static void main(String[] args) throws IOException {
Source source = new FileSource();
source.read(); // file read
}
}
Quick quiz
1. A parent method declares throws IOException. Which of these may the overriding method declare?
- A. throws FileNotFoundException
- B. throws Exception
- C. throws SQLException
- D. throws Throwable
Show answer
A. The override may declare the same exception or a subclass. FileNotFoundException is a subclass of IOException.
2. A parent method declares no exceptions. Can the override throw a RuntimeException?
- A. No
- B. Yes, unchecked exceptions are not restricted
- C. Only if it is declared with throws
- D. Only if the parent is abstract
Show answer
B. The rule applies only to checked exceptions. An override may throw any unchecked exception.
3. A parent method declares no exceptions. The override adds throws IOException. What happens?
- A. It compiles
- B. It compiles with a warning
- C. It does not compile
- D. It fails at runtime
Show answer
C. An override cannot introduce a new checked exception that the parent method did not declare.
Interview questions
What is the rule for exceptions when overriding a method?
Show answer
An overriding method may not declare checked exceptions that are new or broader than those declared by the method it overrides. It may declare the same ones, narrower subclasses, fewer, or none. There is no restriction on unchecked exceptions.
Why does this rule exist?
Show answer
To keep polymorphism safe. A caller holding a parent-type reference writes its try-catch based on what the parent method declares. If a subclass could throw a broader checked exception, that caller would not be handling it, and the compile-time checking of checked exceptions would be defeated. The subclass must be usable anywhere the parent is.
Key Points to Remember
- An override can throw the same checked exception, a subclass of it, or no checked exception at all — never a broader one.
- Overriding methods can throw any unchecked exception freely, regardless of the superclass method's throws clause.
- The rule protects code that calls a method through a superclass or interface reference from unexpected checked exceptions.
- Violating this rule is caught at compile time, not at runtime.
Practice in the playground
Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.
Run the examples
Change an input, predict the result, then compare it with the output. Explain why the result changes.
These examples need a JDK project with their libraries on your computer, so they can be copied but not run here. Codelab runs Python, FastAPI backends with SQLite, plain Java 17 and HTML/CSS/JavaScript.
Written by
WebNest Studio Engineering Team
Practical lessons from developers working with Java, Python, React, databases and production systems.
Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering