Exception Handling in Java
Exception handling in Java offers a well-organised and effective way to handle runtime faults and unusual circumstances, greatly increasing the robustness and dependability of programs.
What is an Exception?
An exception is an occurrence that breaks the regular flow of instructions while a program is running. When a Java method encounters such an unusual circumstance, it generates an exception object that contains details about the error, such as its kind and the state of the program at the moment of the occurrence. After that, the Java runtime system is “thrown” this object to process.
Consider a common scenario like division by zero:
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("Start");
System.out.println("1");
System.out.println("2");
System.out.println("3");
System.out.println(10 / 0); // This line will cause an exception
System.out.println("4"); // This line will not be executed
System.out.println("5"); // This line will not be executed
System.out.println("End"); // This line will not be executed
}
}
Output:
Start
1
2
3
Exception in thread "main" java.lang.ArithmeticException: / by zero
at ExceptionDemo.main(ExceptionDemo.java:7)
The attempt to divide by zero in this instance is an improper arithmetic operation. After identifying this, the Java runtime system generates an ArithmeticException
object and throws it. Since no code specifically handles this, the program is terminated by the default exception handler, which also publishes a stack trace that details the exception, its kind, and the location of the occurrence.
Benefits of Exception Handling
Java’s exception handling provides a number of significant benefits over conventional error-handling methods, such as returning error codes:

- Separating Error-Handling Code: This enhances readability and maintainability by neatly separating error-handling code from “regular” program logic.
- Propagating Errors Up the Call Stack: The ability to propagate exceptions up the chain of method calls until an appropriate handler is located is known as “propagating errors up the call stack.” By doing this, each method in the call chain can avoid manually checking for and passing on error codes.
- Grouping and Differentiating Error Types: In order to enable specific handling for various problem types, it is possible to group similar error types through the exception class hierarchy and differentiate them.
- Graceful Recovery: Instead of stopping suddenly in the event of an error, programs can react and carry on as usual. This is essential for apps that are easy to use and should not crash due to little input errors, for example.
Exception Hierarchy
Java’s exception classes are all a part of a hierarchy that starts with the java.lang.Class throwable
. The two direct subclasses of this class are Exception
and Error
.
- java.lang.Throwable: This is the superclass of all error and exception types in Java. Its methods provide common functionalities for all exceptions, such as retrieving a detailed message or the stack trace.
- Key methods include:
getMessage()
: Returns a detailed message about the exception.toString()
: Returns the class name concatenated with the message. This method is often called when printing an exception object.printStackTrace()
: Prints the stack trace to the standard error stream, showing the sequence of method calls leading to the exception.getCause()
: Returns the underlying cause of this throwable or null if the cause is nonexistent or unknown.fillInStackTrace()
: Fills the stack trace of thisThrowable
object with the current stack trace.
- Key methods include:
- java.lang.Error: Usually unrecoverable by the application itself, these are serious issues that occur beyond of the user’s or programmer’s control.
OutOfMemoryError
,StackOverflowError
, andUnknownError
are a few examples. Programs generally do not catchError
types because recovery is usually not possible. - java.lang.Exception: The regular exceptions that a well-written application should expect and manage fall under the superclass Exception. Program activity, including I/O errors or improper method parameters, is frequently the cause of these. Subclassing
Exception
is a typical way for developers to construct their own unique exception classes. - java.lang.RuntimeException: A significant subclass of
exception
, runtime exceptions reflect common runtime error kinds that frequently point to programming problems.ArithmeticException
(division by zero),NullPointerException
(access to anull
object),ArrayIndexOutOfBoundsException
(invalid array index), andClassCastException
are a few examples.
Checked vs. Unchecked Exceptions
Java separates exceptions into two primary kinds, which affects how the compiler handles them:
- Checked Exceptions:
- It is reasonable to assume that a client software will recover from these predicted occurrences.
- Usually, file not found, network problems, or SQL errors are the causes, which are not directly within the program’s control.
- Checked exceptions are handled by the Java compiler. If a method can throw a checked exception, it must disclose it in its
throws
clause or catch it (usingtry-catch
). If you don’t, a compile-time error will occur. - Common examples:
IOException
,SQLException
,FileNotFoundException
,ClassNotFoundException
,InterruptedException
,CloneNotSupportedException
.
- Unreported Exceptions:
- These are usually unexpected occurrences or programming errors from which an application is typically unable to recover successfully.
- They are
RuntimeException
subclasses. - The handling of unchecked exceptions is not enforced by the Java compiler. They don’t have to be declared in a
throws
clause or explicitly caught by methods. This is due to their nearly universal occurrence and the fact that rigorous programming techniques are frequently required to prevent them. - Common examples:
ArithmeticException
,NullPointerException
,ArrayIndexOutOfBoundsException
,IllegalArgumentException
,NumberFormatException
,ClassCastException
.
Here’s an example demonstrating a custom checked exception and the use of the throws
keyword:
// Custom checked exception class
class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
this.amount = amount;
}
public double getAmount() {
return amount;
}
@Override
public String toString() {
return "InsufficientFundsException: You are short $" + amount;
}
}
// Class that uses the custom exception
class CheckingAccount {
private double balance;
private int accountNumber;
public CheckingAccount(int number) {
this.accountNumber = number;
this.balance = 0;
}
public void deposit(double amount) {
balance += amount;
System.out.println("Deposited $" + amount + ". New balance: $" + balance);
}
// Declares that this method might throw InsufficientFundsException
public void withdraw(double amount) throws InsufficientFundsException {
if (amount <= balance) {
balance -= amount;
System.out.println("Withdrew $" + amount + ". New balance: $" + balance);
} else {
double needs = amount - balance;
// Throws a new instance of the custom exception
throw new InsufficientFundsException(needs);
}
}
public double getBalance() {
return balance;
}
}
// Main class to demonstrate
public class BankDemo {
public static void main(String[] args) {
CheckingAccount account = new CheckingAccount(101);
account.deposit(500);
try {
account.withdraw(100);
account.withdraw(600); // This will cause the InsufficientFundsException
System.out.println("Transaction completed."); // This won't be executed
} catch (InsufficientFundsException e) {
System.out.println("Caught Exception: " + e.getMessage());
e.printStackTrace(); // Prints the stack trace for debugging
} finally {
System.out.println("Finally block executed. Current balance: $" + account.getBalance());
}
System.out.println("Program continues after exception handling.");
}
}
Output:
Deposited $500.0. New balance: $500.0
Withdrew $100.0. New balance: $400.0
Caught Exception: You are short $200.0
InsufficientFundsException: You are short $200.0
at CheckingAccount.withdraw(BankDemo.java:44)
at BankDemo.main(BankDemo.java:65)
Finally block executed. Current balance: $400.0
Program continues after exception handling.
In this example, the withdraw
method explicitly declares that it throws InsufficientFundsException
. The main
method then uses a try-catch-finally
block to handle this checked exception. For cleanup actions, the finally
block is perfect because it is guaranteed to run whether or not an exception arises.