An exception is an object that represents an error or exceptional condition that occurs during the execution of a program. In Java, all exceptions are subclasses of the Throwable
class. The Throwable
class has two main subclasses: Error
and Exception
.
OutOfMemoryError
or StackOverflowError
. These errors are usually not caught and handled in the program.try-catch
block or declare them using the throws
keyword. Examples include IOException
and SQLException
.RuntimeException
and include exceptions like NullPointerException
, ArrayIndexOutOfBoundsException
, etc.The exception hierarchy in Java is as follows:
Throwable
├── Error
│ ├── VirtualMachineError
│ │ ├── OutOfMemoryError
│ │ └── StackOverflowError
│ └── AssertionError
└── Exception
├── RuntimeException
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ArithmeticException
│ └── IllegalArgumentException
└── Checked Exceptions
├── IOException
│ ├── FileNotFoundException
│ └── EOFException
└── SQLException
try-catch
BlockThe try-catch
block is used to catch and handle exceptions. The code that might throw an exception is placed inside the try
block, and the code to handle the exception is placed inside the catch
block.
public class TryCatchExample {
public static void main(String[] args) {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // This will throw an ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught an ArrayIndexOutOfBoundsException: " + e.getMessage());
}
}
}
catch
BlocksYou can have multiple catch
blocks to handle different types of exceptions. The catch
blocks are evaluated in order, and the first matching exception type will be caught.
public class MultipleCatchExample {
public static void main(String[] args) {
try {
int[] arr = null;
System.out.println(arr[0]); // This will throw a NullPointerException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught an ArrayIndexOutOfBoundsException: " + e.getMessage());
} catch (NullPointerException e) {
System.out.println("Caught a NullPointerException: " + e.getMessage());
}
}
}
finally
BlockThe finally
block is used to execute code regardless of whether an exception is thrown or not. It is placed after the try-catch
blocks.
public class FinallyExample {
public static void main(String[] args) {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // This will throw an ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught an ArrayIndexOutOfBoundsException: " + e.getMessage());
} finally {
System.out.println("This code in the finally block will always execute.");
}
}
}
throws
KeywordThe throws
keyword is used to declare that a method might throw one or more exceptions. The calling method is then responsible for handling these exceptions.
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class ThrowsExample {
public static void readFile() throws FileNotFoundException {
File file = new File("nonexistent.txt");
Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
scanner.close();
}
public static void main(String[] args) {
try {
readFile();
} catch (FileNotFoundException e) {
System.out.println("Caught a FileNotFoundException: " + e.getMessage());
}
}
}
throw
KeywordThe throw
keyword is used to explicitly throw an exception. It is usually used in combination with custom exceptions.
public class ThrowExample {
public static void checkAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative.");
}
System.out.println("Age is valid: " + age);
}
public static void main(String[] args) {
try {
checkAge(-5);
} catch (IllegalArgumentException e) {
System.out.println("Caught an IllegalArgumentException: " + e.getMessage());
}
}
}
Logging exceptions is a common practice to record the details of the exception for debugging purposes. You can use the java.util.logging
package or third-party logging frameworks like Log4j.
import java.util.logging.Level;
import java.util.logging.Logger;
public class LoggingExample {
private static final Logger LOGGER = Logger.getLogger(LoggingExample.class.getName());
public static void main(String[] args) {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // This will throw an ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
LOGGER.log(Level.SEVERE, "An exception occurred", e);
}
}
}
Sometimes, you might want to catch an exception, perform some additional actions, and then rethrow the exception to the calling method.
public class RethrowExample {
public static void method1() throws Exception {
try {
int result = 1 / 0; // This will throw an ArithmeticException
} catch (ArithmeticException e) {
System.out.println("Caught an ArithmeticException in method1.");
throw e;
}
}
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
System.out.println("Caught an exception in main: " + e.getMessage());
}
}
}
It is recommended to catch specific exceptions rather than catching the generic Exception
class. This makes the code more readable and easier to maintain.
public class CatchSpecificExample {
public static void main(String[] args) {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]); // This will throw an ArrayIndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught an ArrayIndexOutOfBoundsException: " + e.getMessage());
}
}
}
Ignoring exceptions by having an empty catch
block is a bad practice. Always handle exceptions appropriately or log them for debugging.
// Bad practice
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
// Empty catch block
}
// Good practice
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Caught an ArrayIndexOutOfBoundsException: " + e.getMessage());
}
For application-specific errors, it is a good practice to create custom exceptions. Custom exceptions can provide more meaningful error messages and can be used to handle specific scenarios.
class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}
public class CustomExceptionExample {
public static void validateInput(int num) throws CustomException {
if (num < 0) {
throw new CustomException("Input cannot be negative.");
}
System.out.println("Input is valid: " + num);
}
public static void main(String[] args) {
try {
validateInput(-5);
} catch (CustomException e) {
System.out.println("Caught a CustomException: " + e.getMessage());
}
}
}
Exception handling is an essential part of Java programming. By understanding the fundamental concepts, usage methods, common practices, and best practices of exception handling, you can write more robust and reliable Java programs. Remember to catch specific exceptions, handle exceptions appropriately, and use custom exceptions when necessary. With proper exception handling, you can ensure that your programs can gracefully handle errors and continue to run smoothly.