Checked vs Unchecked Exceptions in Java
Introduction
When something goes wrong while a Java program runs - a file is missing, a number cannot be parsed, an index is past the end of an array - Java signals it with an exception. Java divides exceptions into two kinds, checked and unchecked, and treats them very differently at compile time. This tutorial explains checked vs unchecked exceptions in Java. We will start with what an exception is and what happens when one is not handled. We will then see what makes an exception checked, and the compiler's catch or declare rule. Next we will look at unchecked exceptions and the common ones you will meet. Lastly, we will compare the two side by side, and see when to use each kind for your own exceptions.
(Note - The class hierarchy behind exceptions -
OUTPUT of the above code
Explanation of the output
Checked exceptions - catch or declare
A checked exception is any subclass of
For checked exceptions, the compiler enforces the catch or declare rule: any code which can throw one must either catch it with a
OUTPUT on compiling the above code
OUTPUT of the above code
Explanation of the code
Unchecked exceptions
An unchecked exception is any subclass of
OUTPUT of the above code
None of these statements needed a
The right fix for an unchecked exception is usually to fix the code, not to catch the exception - check for
Note - "Checked" and "unchecked" are decided purely at compile time. At runtime, both kinds are thrown, propagate and are caught in exactly the same way. Which kind should your own exceptions be? When you create your own exception classesClick to read tutorial: Creating Custom Exceptions in Java with Examples, a common guideline - from Effective Java and the Java documentation - is -
(Note - The class hierarchy behind exceptions -
Throwable, Error, Exception and RuntimeException - is explained in detail in Understanding the Exception Hierarchy in JavaClick to read tutorial on the Java exception hierarchy.)
What is an exception?
An exception is an object which describes an unusual event that stops the normal flow of a program. When it happens, Java throws the exception: the current method stops, and Java looks back through the call stackClick to read tutorial: Java Methods: Defining, Calling and Returning Values for code that catches it. If no method catches it, the thread ends, and Java prints the exception's type, its message and the stack trace - the chain of method calls which led to it.
An exception that nobody catches
//Uncaught.java
public class Uncaught {
static int parseAge(String text) {
return Integer.parseInt(text);
}
static void register(String name, String ageText) {
int age = parseAge(ageText);
System.out.println(name + " registered, age " + age);
}
public static void main(String[] args) {
register("Tom", "32");
register("Harry", "thirty");
System.out.println("This line is never reached");
}
}
Tom registered, age 32 Exception in thread "main" java.lang.NumberFormatException: For input string: "thirty" at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67) at java.base/java.lang.Integer.parseInt(Integer.java:564) at java.base/java.lang.Integer.parseInt(Integer.java:661) at Uncaught.parseAge(Uncaught.java:4) at Uncaught.register(Uncaught.java:8) at Uncaught.main(Uncaught.java:14)
- The first call worked. In the second,
Integer.parseInt("thirty")threw aNumberFormatException. - Nobody caught it, so it passed up through
parseAge(),register()andmain(), and the program ended - the last line never ran. - Read a stack trace from the top: the first line is the exception and its message, and the first line mentioning your code (
Uncaught.parseAge) is usually where to start looking.
java.lang.Throwable. Its two direct subclasses are Error, for serious problems in the JVM itself (such as OutOfMemoryError), which programs should not try to handle, and Exception, for problems a program can reasonably handle.
Exception which is not a subclass of RuntimeException. Checked exceptions describe problems that are outside the program's control but are expected to happen sometimes - a file which does not exist, a network connection which drops, a database which is unavailable. Common examples are IOException, FileNotFoundException, SQLException and InterruptedException.For checked exceptions, the compiler enforces the catch or declare rule: any code which can throw one must either catch it with a
try/catch block, or declare it with throws in the method's signature, passing the responsibility to the caller. Code which does neither does not compile -
A checked exception that is neither caught nor declared
//ReadConfig.java
import java.nio.file.Files;
import java.nio.file.Path;
public class ReadConfig {
public static void main(String[] args) {
String text = Files.readString(Path.of("config.txt"));
System.out.println(text);
}
}
ReadConfig.java:7: error: unreported exception IOException; must be caught or declared to be thrown
String text = Files.readString(Path.of("config.txt"));
^
1 error
Files.readString() declares throws IOException, so the compiler insists that we deal with it. Here are both ways -
Handling a checked exception: catch it, or declare it
//ReadConfigFixed.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
public class ReadConfigFixed {
// Option 1: catch the exception and handle it here
static String readOrDefault(String file) {
try {
return Files.readString(Path.of(file));
} catch (NoSuchFileException e) {
System.out.println(" " + file + " not found - using defaults");
return "mode=default";
} catch (IOException e) {
System.out.println(" could not read " + file + ": " + e.getMessage());
return "mode=default";
}
}
// Option 2: declare the exception and let the caller decide
static String readRequired(String file) throws IOException {
return Files.readString(Path.of(file));
}
public static void main(String[] args) {
System.out.println("Config: " + readOrDefault("config.txt"));
try {
System.out.println("Config: " + readRequired("config.txt"));
} catch (IOException e) {
System.out.println("Required config missing: " + e);
}
}
}
config.txt not found - using defaults Config: mode=default Required config missing: java.nio.file.NoSuchFileException: config.txt
readOrDefault()catches the exception and recovers with a default value, so its callers never see it.readRequired()declaresthrows IOException, so it compiles without atry- but now every caller must catch or declare it, asmain()does.- The more specific
NoSuchFileException(a subclass ofIOException) is caught first. Catching, multi-catch andfinallyare covered in try, catch, finally and multi-catchClick to read tutorial: Java try, catch, finally and Multi-catch with Examples, andthrowsin throw vs throwsClick to read tutorial: throw vs throws in Java with Examples.
RuntimeException (and, strictly, any Error). The compiler does not check them: methods do not have to declare them, and callers do not have to catch them. They usually signal a programming bug - something the code should have prevented - rather than an outside problem. The most common ones are -NullPointerException- calling a method or reading a field on anullreference.ArrayIndexOutOfBoundsExceptionandStringIndexOutOfBoundsException- an index outside the valid range.ArithmeticException- such as integer division by zero.NumberFormatException- text which is not a valid number, passed toparseInt()and similar methods.IllegalArgumentExceptionandIllegalStateException- a method was given a bad argument, or called at the wrong time.ClassCastException- an invalid cast between object types.
Common unchecked exceptions
//UncheckedDemo.java
import java.util.List;
public class UncheckedDemo {
static String name = null;
static int[] scores = {72, 85, 90};
static Object value = "text";
public static void main(String[] args) {
String[] labels = {"name.length()", "scores[3]", "10 / 0", "parseInt(\"12a\")", "(Integer) value", "List.of(...).add()"};
for (int i = 0; i < labels.length; i++) {
try {
switch (i) {
case 0 -> System.out.println(name.length());
case 1 -> System.out.println(scores[3]);
case 2 -> System.out.println(10 / 0);
case 3 -> System.out.println(Integer.parseInt("12a"));
case 4 -> System.out.println((Integer) value);
case 5 -> List.of("Tom").add("Harry");
}
} catch (RuntimeException e) {
System.out.printf("%-20s -> %s: %s%n", labels[i], e.getClass().getSimpleName(), e.getMessage());
}
}
}
}
name.length() -> NullPointerException: Cannot invoke "String.length()" because "UncheckedDemo.name" is null
scores[3] -> ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
10 / 0 -> ArithmeticException: / by zero
parseInt("12a") -> NumberFormatException: For input string: "12a"
(Integer) value -> ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer (java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
List.of(...).add() -> UnsupportedOperationException: null
try block to compile - the program catches them only so that it can show them all. Notice the helpful NullPointerException messageClick to read tutorial on Java 14 helpful NullPointerExceptions - since Java 14, it names exactly which variable was null. The UnsupportedOperationException has no message (null); it was thrown because lists created by List.of() cannot be changed.The right fix for an unchecked exception is usually to fix the code, not to catch the exception - check for
null, check the index against the length, validate input before parsing it.
Checked vs unchecked - comparison
| Checked | Unchecked | |
| Superclass | Exception (but not RuntimeException) | RuntimeException (and Error) |
| Checked by the compiler? | Yes - must be caught or declared | No |
| Usually means | An expected problem outside the program's control | A programming bug, or invalid input that should have been checked |
| Typical response | Recover: retry, use a default, report to the user | Fix the code; let it fail fast |
| Examples | IOException, SQLException, InterruptedException | NullPointerException, IllegalArgumentException, ArithmeticException |
Note - "Checked" and "unchecked" are decided purely at compile time. At runtime, both kinds are thrown, propagate and are caught in exactly the same way. Which kind should your own exceptions be? When you create your own exception classesClick to read tutorial: Creating Custom Exceptions in Java with Examples, a common guideline - from Effective Java and the Java documentation - is -
- Use a checked exception when the caller can reasonably be expected to recover - for example, an
InsufficientFundsExceptionwhich the caller can report to the user. - Use an unchecked exception for programming errors and broken preconditions - such as a negative quantity passed to a method that requires a positive one.
- When in doubt, many modern libraries and frameworks prefer unchecked exceptions, because checked exceptions force every layer of code in between to declare them, and do not work well with lambdas and streams.
Exception outside RuntimeException - and the compiler's catch or declare rule, handling an IOException both ways. Next we met the common unchecked exceptions, which signal programming bugs and need neither catching nor declaring. We then compared the two kinds, and lastly saw when to choose each for your own exceptions. In the next tutorial, we will handle exceptions with try, catch, finally and multi-catchClick to read tutorial: Java try, catch, finally and Multi-catch with Examples.Core Java Tutorials
BigInteger and BigDecimal in Java with ExamplesClick to read tutorial: BigInteger and BigDecimal in Java with Examples Java try, catch, finally and Multi-catch with ExamplesClick to read tutorial: Java try, catch, finally and Multi-catch with Examples throw vs throws in Java with ExamplesClick to read tutorial: throw vs throws in Java with Examples Creating Custom Exceptions in Java with ExamplesClick to read tutorial: Creating Custom Exceptions in Java with Examples Java try-with-resources and AutoCloseable ExplainedClick to read tutorial: Java try-with-resources and AutoCloseable Explained Java Exception Handling Best PracticesClick to read tutorial: Java Exception Handling Best Practices Java Math Class: Essential Methods with ExamplesClick to read tutorial: Java Math Class: Essential Methods with Examples
BigInteger and BigDecimal in Java with ExamplesClick to read tutorial: BigInteger and BigDecimal in Java with Examples Java try, catch, finally and Multi-catch with ExamplesClick to read tutorial: Java try, catch, finally and Multi-catch with Examples throw vs throws in Java with ExamplesClick to read tutorial: throw vs throws in Java with Examples Creating Custom Exceptions in Java with ExamplesClick to read tutorial: Creating Custom Exceptions in Java with Examples Java try-with-resources and AutoCloseable ExplainedClick to read tutorial: Java try-with-resources and AutoCloseable Explained Java Exception Handling Best PracticesClick to read tutorial: Java Exception Handling Best Practices Java Math Class: Essential Methods with ExamplesClick to read tutorial: Java Math Class: Essential Methods with Examples