Java Exception Handling
Exceptions are errors in a program, but not all errors are exceptions, and sometimes errors can be avoided.
For example, if your code is missing a semicolon, the result will be an error message java.lang.Error; if you use System.out.println(11/0), you will get a java.lang.ArithmeticException because you are dividing by zero.
There are many reasons for exceptions, usually including the following categories:
- The user entered illegal data.
- The file to be opened does not exist.
- The connection is interrupted during network communication, or the JVM memory overflows.
Some of these exceptions are caused by user errors, some by program errors, and others by physical errors.
To understand how Java exception handling works, you need to grasp the following three types of exceptions:
- Checked Exceptions: The most representative checked exceptions are those caused by user errors or problems that programmers cannot foresee. For example, when trying to open a non-existent file, an exception occurs, and these exceptions cannot be simply ignored at compile time.
- Runtime Exceptions: Runtime exceptions are those that can be avoided by programmers. Unlike checked exceptions, runtime exceptions can be ignored at compile time.
- Errors: Errors are not exceptions but issues beyond the control of programmers. Errors are usually ignored in code. For example, when a stack overflow occurs, an error happens, and they are not detectable at compile time.
Hierarchy of Exception Classes
All exception classes are subclasses of the java.lang.Exception class.
The Exception class is a subclass of the Throwable class. In addition to the Exception class, Throwable has another subclass, Error.
Java programs typically do not catch errors. Errors usually occur in serious failures and are beyond the scope of Java program handling.
Error indicates errors that occur at runtime.
For example, JVM memory overflow. Generally, programs cannot recover from errors.
Exception classes have two main subclasses: IOException and RuntimeException.
In the built-in classes of Java (which will be explained next), there are many common checked and unchecked exceptions.
Built-in Exception Classes in Java
The Java language defines some exception classes in the java.lang standard package.
Subclasses of the standard runtime exception class are the most common exception classes. Since the java.lang package is loaded by default into all Java programs, most exceptions inherited from the runtime exception class can be used directly.
Java also defines some other exceptions based on various class libraries. The following table lists Java's unchecked exceptions.
Exception | Description |
---|---|
ArithmeticException | Thrown when an exceptional arithmetic condition has occurred. For example, an integer "divide by zero" throws an instance of this class. |
ArrayIndexOutOfBoundsException | Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array. |
ArrayStoreException | Thrown to indicate that an attempt has been made to store the wrong type of object into an array of objects. |
ClassCastException | Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance. |
IllegalArgumentException | Thrown to indicate that a method has been passed an illegal or inappropriate argument. |
IllegalMonitorStateException | Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor. |
IllegalStateException | Signals that a method has been invoked at an illegal or inappropriate time. In other words, the Java environment or Java application is not in an appropriate state for the requested operation. |
IllegalThreadStateException | Thrown to indicate that a thread is not in an appropriate state for the requested operation. |
IndexOutOfBoundsException | Thrown to indicate that an index of some sort (such as to an array, to a string, or to a vector) is out of range. |
NegativeArraySizeException | Thrown if an application tries to create an array with negative size. |
NullPointerException | Thrown when an application attempts to use null in a case where an object is required. |
NumberFormatException | Thrown when an application tries to convert a string to a numeric type, but the string does not have an appropriate format. |
SecurityException | Thrown by the security manager to indicate a security violation. |
StringIndexOutOfBoundsException | Thrown by methods in the String class to indicate that an index is either negative or greater than the size of the string. |
UnsupportedOperationException | Thrown to indicate that the requested operation is not supported. |
The table below lists the checked exception classes defined in the java.lang
package in Java.
Exception | Description |
---|---|
ClassNotFoundException | Thrown when an application tries to load a class through its string name, but no definition for the class with the specified name could be found. |
CloneNotSupportedException | Thrown when the clone method in class Object is called to clone an object, but that object's class does not implement the Cloneable interface. |
IllegalAccessException | Thrown when an application tries to reflectively create an instance, set or get a field, or invoke a method, but the currently executing method does not have access to the definition of the specified class, field, method, or constructor. |
InstantiationException | Thrown when an application tries to create an instance of a class using the newInstance method in class Class , but the specified class object cannot be instantiated because it is an interface or an abstract class. |
InterruptedException | Thrown when a thread is waiting, sleeping, or otherwise occupied, and the thread is interrupted, either before or during the activity. |
NoSuchFieldException | Thrown if the specified field does not exist. |
NoSuchMethodException | Thrown if the specified method does not exist. |
Exception Methods
The following list outlines the main methods of the Throwable
class:
No. | Method and Description |
---|---|
1 | public String getMessage() <br> Returns a detailed message about the exception that has occurred. This message is initialized in the Throwable constructor. |
2 | public Throwable getCause() <br> Returns a Throwable object representing the cause of the exception. |
3 | public String toString() <br> Returns a short description of this Throwable . |
4 | public void printStackTrace() <br> Prints this Throwable and its backtrace to the standard error stream. |
5 | public StackTraceElement[] getStackTrace() <br> Returns an array containing stack trace elements. The zeroth element represents the top of the stack, and the last element represents the bottom of the method call stack. |
6 | public Throwable fillInStackTrace() <br> Fills in the execution stack trace, adding to any previous information. |
Catching Exceptions
Exceptions can be caught using try
and catch
keywords. The try/catch
block is placed where the exception is likely to occur.
The code within the try/catch
block is referred to as protected code. The syntax for using try/catch
is as follows:
try
{
// Program code
}catch(ExceptionName e1)
{
// Catch block
}
The catch
statement contains the declaration of the type of exception to be caught. When an exception occurs within the protected code, the catch
block that follows the try
block is checked.
If the exception that occurred is included in the catch
block, it is passed to the catch
block, similar to passing an argument to a method.
Example
The following example declares an array with two elements and attempts to access the fourth element, which throws an exception.
ExcepTest.java File Code:
// File name : ExcepTest.java
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try{
int a[] = new int[2];
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
System.out.println("Out of the block");
}
}
The above code, when compiled and run, produces the following output:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block
Multiple Catch Blocks
A try
block can be followed by multiple catch
blocks, which is known as multiple catch blocks.
The syntax for multiple catch blocks is as follows:
try{
// Program code
}catch(ExceptionType1 e1){
// Program code
}catch(ExceptionType2 e2){
// Program code
}catch(ExceptionType3 e3){
// Program code
}
The code snippet above contains 3 catch blocks.
You can add any number of catch blocks after a try statement.
If an exception occurs within the protected code, it is thrown to the first catch block.
If the data type of the thrown exception matches ExceptionType1, it will be caught here.
If it does not match, it will be passed to the second catch block.
This continues until the exception is caught or it passes through all the catch blocks.
Example
This example demonstrates how to use multiple try/catch blocks.
try {
file = new FileInputStream(fileName);
x = (byte) file.read();
} catch(FileNotFoundException f) { // Not valid!
f.printStackTrace();
return -1;
} catch(IOException i) {
i.printStackTrace();
return -1;
}
throws/throw Keywords:
If a method does not catch a checked exception, the method must declare it using the throws keyword. The throws keyword appears at the end of a method's signature.
You can also throw an exception using the throw keyword, whether it is newly instantiated or just caught.
The following method declaration throws a RemoteException:
import java.io.*;
public class className
{
public void deposit(double amount) throws RemoteException
{
// Method implementation
throw new RemoteException();
}
// Remainder of class definition
}
A method can declare to throw multiple exceptions, separated by commas.
For example, the following method declares to throw RemoteException and InsufficientFundsException:
import java.io.*;
public class className
{
public void withdraw(double amount) throws RemoteException,
InsufficientFundsException
{
// Method implementation
}
// Remainder of class definition
}
finally Keyword
The finally keyword is used to create a block of code that follows a try block.
The code in the finally block will always be executed regardless of whether an exception occurs.
In the finally block, you can run cleanup or finalization code.
The finally block appears after the catch block, with the following syntax:
try{
// Program code
}catch(ExceptionType1 exceptionVariable1){
// Program code
}catch(ExceptionType2 exceptionVariable2){
// Program code
}finally{
// Program code
}
Example
ExcepTest.java File Code:
public class ExcepTest{
public static void main(String args[]){
int a[] = new int[2];
try{
System.out.println("Access element three :" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("Exception thrown :" + e);
}
finally{
a[0] = 6;
System.out.println("First element value: " +a[0]);
System.out.println("The finally statement is executed");
}
}
}
The above example compiles and runs with the following result:
Exception thrown :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed
Note the following:
- A catch block cannot exist without a try block.
- Adding a
finally
block aftertry/catch
is not mandatory. - A
try
block cannot be followed by neither acatch
block nor afinally
block. - No code can be inserted between
try
,catch
, andfinally
blocks.
try-with-resources
After JDK7, Java introduced the try-with-resources
syntactic sugar to open resources and ensure that each resource is automatically closed after the statement execution.
Before JDK7, all opened system resources, such as streams, files, or Socket connections, needed to be manually closed by the developer, otherwise it would lead to resource leakage.
try (resource declaration) {
// using the resource
} catch (ExceptionType e1) {
// exception block
}
In the above syntax, try
is used to declare and instantiate resources, and catch
is used to handle any exceptions that may be thrown when closing the resources.
Note: The try-with-resources statement closes all resources that implement the AutoCloseable interface.
Example
import java.io.*;
public class tutorialproTest {
public static void main(String[] args) {
String line;
try(BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
while ((line = br.readLine()) != null) {
System.out.println("Line =>"+line);
}
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
}
}
}
The output of the above example is:
IOException in try block =>test.txt (No such file or directory)
In the above example, we instantiate a BufferedReader object to read data from the test.txt
file.
Declaring and instantiating the BufferedReader in the try-with-resources statement ensures that the resource is closed after the statement execution, regardless of whether the try statement completes normally or abruptly.
If an exception occurs, it can be handled using the catch block.
Now, let's see what it looks like without using try-with-resources and instead using finally to close the resource. The overall code is much more extensive and complex:
Example
import java.io.*;
class tutorialproTest {
public static void main(String[] args) {
BufferedReader br = null;
String line;
try {
System.out.println("Entering try block");
br = new BufferedReader(new FileReader("test.txt"));
while ((line = br.readLine()) != null) {
System.out.println("Line =>"+line);
}
} catch (IOException e) {
System.out.println("IOException in try block =>" + e.getMessage());
} finally {
System.out.println("Entering finally block");
try {
if (br != null) {
br.close();
}
} catch (IOException e) {
System.out.println("IOException in finally block =>"+e.getMessage());
}
}
}
}
Entering try block
IOException in try block => test.txt (No such file or directory)
Entering finally block
Handling Multiple Resources with try-with-resources
The try-with-resources statement can declare multiple resources by separating them with semicolons ;
:
Example
import java.io.*;
import java.util.*;
class tutorialproTest {
public static void main(String[] args) throws IOException{
try (Scanner scanner = new Scanner(new File("testRead.txt"));
PrintWriter writer = new PrintWriter(new File("testWrite.txt"))) {
while (scanner.hasNext()) {
writer.print(scanner.nextLine());
}
}
}
}
This example uses a Scanner object to read a line from the file "testRead.txt" and writes it to a new file "testWrite.txt".
When multiple resources are declared, the try-with-resources
statement closes these resources in the reverse order. In this case, the PrintWriter object is closed first, followed by the Scanner object.
Declaring Custom Exceptions
In Java, you can define your own exceptions. Keep the following points in mind when writing your own exception classes:
All exceptions must be subclasses of Throwable.
If you want to write a checked exception class, you need to inherit from the Exception class.
If you want to write a runtime exception class, you need to inherit from the RuntimeException class.
You can define your own exception class as follows:
class MyException extends Exception{
}
Inheriting from the Exception class creates a checked exception class.
The following InsufficientFundsException class is a user-defined exception class that extends Exception.
An exception class, like any other class, contains variables and methods.
Example
The following example simulates a bank account, identified by the card number, allowing deposit and withdrawal operations.
InsufficientFundsException.java File Code:
// File name InsufficientFundsException.java
import java.io.*;
// Custom exception class, inheriting from Exception class
public class InsufficientFundsException extends Exception
{
// The amount here stores the deficiency when an exception occurs (withdrawing more than the balance)
private double amount;
public InsufficientFundsException(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
}
To demonstrate how to use our custom exception class,
the following CheckingAccount class includes a withdraw() method that throws an InsufficientFundsException.
CheckingAccount.java File Code:
// File name CheckingAccount.java
import java.io.*;
// This class simulates a bank account
public class CheckingAccount
{
// balance is the balance, number is the card number
private double balance;
private int number;
public CheckingAccount(int number)
{
this.number = number;
}
// Method: Deposit money
public void deposit(double amount)
{
balance += amount;
}
// Method: Withdraw money
public void withdraw(double amount) throws InsufficientFundsException
InsufficientFundsException { if(amount <= balance) { balance -= amount; } else { double needs = amount - balance; throw new InsufficientFundsException(needs); } } // Method: Return balance public double getBalance() { return balance; } // Method: Return card number public int getNumber() { return number; }
The BankDemo program demonstrates how to call the deposit() and withdraw() methods of the CheckingAccount class.
BankDemo.java File Code:
// File name BankDemo.java
public class BankDemo
{
public static void main(String [] args)
{
CheckingAccount c = new CheckingAccount(101);
System.out.println("Depositing $500...");
c.deposit(500.00);
try
{
System.out.println("\nWithdrawing $100...");
c.withdraw(100.00);
System.out.println("\nWithdrawing $600...");
c.withdraw(600.00);
}catch(InsufficientFundsException e)
{
System.out.println("Sorry, but you are short $"
+ e.getAmount());
e.printStackTrace();
}
}
}
Compile the above three files and run the BankDemo program, the result is as follows:
Depositing $500...
Withdrawing $100...
Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
at CheckingAccount.withdraw(CheckingAccount.java:25)
at BankDemo.main(BankDemo.java:13)
Common Exceptions
In Java, two types of exceptions and errors are defined.
JVM (Java Virtual Machine) Exceptions: Exceptions or errors thrown by the JVM. For example: NullPointerException class, ArrayIndexOutOfBoundsException class, ClassCastException class.
Program-Level Exceptions: Exceptions thrown by the program or API. For example: IllegalArgumentException class, IllegalStateException class.