Exception handling is a mechanism that safely handles abnormal situations (no file, incorrect input, network failure, etc.) encountered during program execution. Java provides two types of exception systems: try/catch/finally and checked/unchecked.
Learning Objectives
- Know the flow of
try/catch/finally - Know the difference between checked exception and unchecked(RuntimeException)
- Exceptions can be delegated to
throws - Create custom exception classes
- Look at multi-catch and exception chaining (
cause)
Key concepts
1) try / catch / finally
try {
int n = Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println("Not a number:" + e.getMessage());
} finally {
System.out.println("always run");
}
finally runs unconditionally regardless of exceptions (used for resource cleanup).
2) Checked vs Unchecked
| Type | Example | Obligation processing |
|---|---|---|
Checked (Exception descendant) |
IOException, SQLException |
Must declare try/catch or throws |
Unchecked (RuntimeException descendant) |
NullPointerException, IllegalArgumentException |
No forced processing |
| Error | OutOfMemoryError |
Attempt to process X (system error) |
3) throws
static void readFile(String path) throws IOException {
Files.readString(Path.of(path));
}
This is a keyword that tells the caller to “handle this exception.”
4) Custom Exception
class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String msg) { super(msg); }
}
Creating exception classes with clear domain semantics allows callers to clearly plan their processing flow.
5) Multi-catch
try {
...
} catch (IOException | NumberFormatException e) {
System.out.println("Failure:" + e.getMessage());
}
Starting with JDK 7+, multiple exception types are allowed in a single catch clause.
See as example
Example 1 — TryCatch.java: Basic flow
public class TryCatch {
public static void main(String[] args) {
try {
int n = Integer.parseInt("abc");
System.out.println(n);
} catch (NumberFormatException e) {
System.out.println("Not a number:" + e.getMessage());
} finally {
System.out.println("End Cleanup");
}
System.out.println("program continues");
}
}
Execution result
Not a number: For input string: "abc"
End Cleanup
program continues
Note: If catch catches an exception, the program does not die and continues executing the next line.
Example 2 — MultiCatch.java: Handling multiple exceptions simultaneously
public class MultiCatch {
public static void main(String[] args) {
Object[] inputs = { "10", "abc", null };
for (Object o : inputs) {
try {
String s = (String) o;
int n = Integer.parseInt(s.trim());
System.out.println("Success:" + n);
} catch (NullPointerException | NumberFormatException e) {
System.out.println("Failure:" + e.getClass().getSimpleName());
}
}
}
}
Execution result
Success: 10
Failure: NumberFormatException
Failure: NullPointerException
Note: If two exceptions have semantically the same handling, they can be combined on one line.
Example 3 — ThrowsAndCustom.java : throws + user exception
public class ThrowsAndCustom {
static class InvalidAgeException extends RuntimeException {
public InvalidAgeException(String msg) { super(msg); }
}
static void register(String name, int age) {
if (age < 0) {
throw new InvalidAgeException("Age is 0 or greater:" + age);
}
System.out.println("Registration:" + name + " (" + age + ")");
}
public static void main(String[] args) {
try {
register("exponent", 21);
register("unknown", -1);
} catch (InvalidAgeException e) {
System.out.println("Reject:" + e.getMessage());
}
}
}
Execution result
Registration: Index (21)
Rejected: Age greater than or equal to 0: -1
Note: The unchecked exception (RuntimeException inheritance) does not require the declaration of throws.
Example 4 — ExceptionChaining.java: Preserving exception causes
public class ExceptionChaining {
static void load() {
try {
Integer.parseInt("nope");
} catch (NumberFormatException low) {
throw new IllegalStateException("Failed to load settings", low);
}
}
public static void main(String[] args) {
try {
load();
} catch (IllegalStateException e) {
System.out.println("Outside:" + e.getMessage());
System.out.println("Cause:" + e.getCause());
}
}
}
Execution result
Outside: Failed to load settings
Caused by: java.lang.NumberFormatException: For input string: "nope"
Note: Patterns that wrap low-level exceptions in higher-level semantics while preserving the cause are very useful for debugging.
Common Mistakes
- Catch and ignore all exceptions with
catch (Exception e)(silent failure) - Empty catch block → Bury the problem
- Overwrite the body return using
returninfinally - Attempting to ignore checked exceptions without throwing or trying/catching them (compilation error)
- Overuse of
RuntimeExceptionwithout domain meaning (user-defined exceptions are clearer)
Cleanup
- Exceptions are objects that represent abnormal flows.
- Checked must be processed, unchecked is optional.
- Clarify domain semantics with custom exceptions
- Preserving the cause makes tracking easier.
Try it yourself
cd 04_Exception_Input/Output/15_Exception-handling/src
javac ThrowsAndCustom.java
java ThrowsAndCustom