Exception handling in Java

Saurav Kumar
7 min readSep 20, 2023

--

Exception handling in Java allows us to gracefully handle errors or exceptional situations that may occur during program execution. Java uses a try-catch block to handle exceptions. Here’s a basic overview:

1. Try Block: You enclose the code that might throw an exception inside a ‘try’ block.

try{
// Code that may throw an exception
}catch(ExceptionType1 e1){
// Handle ExceptionType1
}catch(ExcpetionType2 e2){
// Handle ExceptionType2
}finally{
//Optinal: Code that runs regardless of whether an exception occured or not
}

2. Catch Block: You can have multiple ‘catch’ blocks to handle different types of exceptions. The catch blocks are evaluated in order, and the first one that matches the thrown exception’s class is executed.

3. Finally Block: The ‘finally’ block is optional and is used for code that must be executed regardless of whether an exception is thrown or not.

4. throw:

  • ‘throw’ is a keyword in Java used to throw an exception within your code explicitly. It is used when you encounter an exceptional situation that you want to handle or propagate as an exception.
  • You use ‘throw’ followed by an exception object to indicate that a specific exception has occurred.
if(someCoditionIsMet){
throw new CustomException("This is a Custom Exception.")
}

5. throws:

  • ‘throws’ is a keyword used in method declarations to specify that a method might throw one or more exceptions. It is used to indicate the exceptions that a method can potentially throw but doesn’t handle.
  • When a method declares ‘throws’ for a particular exception, it means that it’s the responsibility of the method calling this one to handle that exception or propagate it further.
public void someMethod() throws CustomException{
// Code that might throw CustomException
}

When you call ‘someMethod()’, you need to either use a ‘try-catch’ block to handle ‘CustomException’ or declare ‘throws CustomException’ in the method that calls ‘someMethod()’.

“throw” is used to explicitly raise an exception within your code, while “throws” is used in method declarations to specify the exceptions that the method might throw, leaving the responsibility of handling those exceptions to the calling code.

Types of Exception Handling:

In Java, exceptions are categorized into two main types: checked exceptions and unchecked exceptions (also known as runtime exceptions). Here’s an explanation of each type:

  1. Checked Exceptions:
  • Checked exceptions are exceptions that the Java compiler forces you to handle or declare in your code. They are subclasses of the ‘Exception’ class but not of ‘RuntimeException’.
  • These exceptions typically represent external factors that can cause issues in your program and are beyond your control. For example, file I/O, network communication, or database operations may result in checked exceptions.
  • You must either use a ‘try-catch’ block to handle checked exceptions or declare them using the ‘throws’ clause in your method signature.

Example of checked exceptions:

  • ‘IOException’: Thrown when an input/output operation fails.
  • ‘SQLException’: Thrown for database-related errors.
  • ‘FileNotFoundException’: Thrown when trying to access a file that doesn’t exist.
// This code won't compile without handling or declaring the IOException
import java.io.*;
public class FileRead{
public static void main(String args[]){
FileReader file = new FileReader("file.txt"); // Compiler error
}
}

2. Unchecked Exceptions (Runtime Exceptions):

  • Unchecked exceptions are exceptions that the Java compiler doesn’t force you to handle or declare explicitly. They are typically caused by programming errors within your code and can be prevented by writing better code.
  • Unchecked exceptions are subclasses of ‘RuntimeException’.
  • Common programming errors that lead to unchecked exceptions include null pointer dereferences, array index out of bounds, and arithmetic division by zero.

Example of unchecked exceptions:

  • ‘NullPointerException’: Thrown when attempting to access a member or method of an object that is ‘null’.
  • ‘ArrayIndexOutOfBoundException’: Thrown when accessing an array element with an invalid index.
  • ArithmeticException’: Thrown when divided by zero.
public class DivisionExample{
public static void main(String args[]){
int result = 10 / 0; // This compiles without errors
}
}

It’s essential to understand the distinction between checked and unchecked exceptions because it influences how you handle and document errors in your Java code. Checked exceptions are generally used for scenarios where it’s possible to recover from the exception, while unchecked exceptions indicate more severe issues that should be fixed in the code.

In practice, most custom exceptions you create in your Java applications will likely be subclasses of either ‘Exception’ (for checked exceptions) or ‘RuntimeExceptions’ (for unchecked exceptions) depending on how you want to handle them in your code.

In summary, checked exceptions are related to compile-time exceptions because the compiler ensures that you deal with them at compile-time. Failure to do so results in a compilation error. On the other hand, unchecked exceptions are related to runtime exceptions because they often indicate errors in your program’s logic and are not enforced by the compiler. They can lead to runtime errors when your code is executed.

Custom Exceptions in Java

We can create custom-checked and unchecked exceptions in Java by defining new classes that either extend ‘java.lang.Exception’ (for checked exceptions) or ‘java.lang.RuntimeException’ (for unchecked exceptions). Here’s how you can create both types:

Custom Checked Exception (Extending ‘java.lang.Exception’):

public class CustomCheckedException extends Exception{
public CustomCheckedException(){
super("This is a custom checked exception");
}

// You can add custom constructors and methods as needed
}

In the example above, ‘CustomCheckedException’ is a custom-checked exception class. It extends the ‘Exception’ class, and you can customize it by adding constructors and methods as required.

Custom Unchecked Exception (Extending ‘java.lang.RuntimeException’):

public class CustomUncheckedException extends RuntimeException{
public CustomUncheckedException(){
super("This is a custom unchecked exception.");
}
}

In this example, ‘CustomUncheckedException’ is a custom unchecked exception class. It extends ‘RuntimeException’, which is a subclass of ‘Exception’, but unlike checked exceptions, you don’t need to explicitly declare or handle unchecked exceptions.

To use your custom exceptions, you can throw them in your code as needed:

public class Exception{
public static void main(String args[]){
try{
// Simulate throwing a custom checked exception
throw new CustomCheckedException();
}catch(CustomCheckedException e){
System.out.println("Caught CustomCheckedException: " + e.getMessage());
}

// Simulate throwing a custom unchecked exception
throw new CustomUncheckedException();
}
}

In the code above, we create instances of our custom exceptions and throw them. When dealing with checked exceptions, you should catch or declare them; however, unchecked exceptions can be thrown without explicit handling or declaration.

Remember to choose whether to create a checked or unchecked custom exception based on whether you want to enforce handling or declaration in the code or leave it to the discretion of the developer.

Checked exceptions are subclasses of the Exception class but not of RuntimeException:

  1. Checked Exceptions: Checked exceptions are exceptions in Java that you are required to either handle or declare. They are subclasses of the ‘Exception’ class, which is part of the broader exception hierarchy in Java.
  2. Subclasses of Exception: When we say that checked exceptions are “subclasses of the Exception class,” it means that all checked exceptions directly or indirectly inherit from the ‘Exception’ class. In other words, they are part of the inheritance chain that starts with ‘Exception’.
  3. But not RuntimeException: Checked exceptions are distinct from unchecked exceptions (also known as runtime exceptions). While checked exceptions extend from ‘Exception’, they do not extend from ‘RuntimeException’.

In summary, this statement highlights the difference between two main categories of exceptions in Java:

  • Checked exceptions are required to be explicitly handled or declared. They are part of the ‘Exception’ hierarchy.
  • Unchecked exceptions (runtime exceptions) do not need to be explicitly handled or declared. They are part of the ‘RuntimeException’ hierarchy.

Errors in Java:

In Java, errors are exceptional situations that typically indicate severe problems during program execution. Errors are not meant to be caught or handled by your application because they often represent issues that are beyond your control or indicate critical problems that make it unsafe to continue running the program. Here are some common types of errors in Java:

  1. OutOfMemoryError: This error occurs when the Java Virtual Machine (JVM) runs out of memory, typically due to excessive memory allocation or resource leaks.
  2. StackOverflowError: It happens when the call stack, used for method call and return tracking, exceeds its limit, This often occurs in cases of infinite recursion.
  3. NoClassDefFoundError: This error occurs when the JVM can not find a class at runtime, even though it was available during compilation. It might indicate issues with classpath or missing dependencies.
  4. InternalError: These are typically JVM-specific errors that indicate internal problems with the Java runtime environment itself. They are not meant for application-level handling.
  5. ThreadDeath: This error is thrown to stop a thread when it is requested to terminate. It’s a way to stop a thread gracefully, although it’s rarely used in modern Java programming.
  6. AbstractMethodError: This error occurs when an abstract method declared in a superclass does not have a concrete implementation in a subclass.
  7. NoSuchMethodError: This error happens when the JVM tries to invoke a method that doesn’t exist in a class.
  8. UnsatisfiedLinkError: It occurs when a native method (a method implemented in a non-Java language like C or C++) cannot be found or loaded.
  9. ExceptionInIntializerError: This error occurs when an exception is thrown while initializing a class. It’s often related to static initializers.

It’s important to note that errors are typically not caught or handled in your Java code because they indicate severe issues that may lead to program instability. Instead, these errors are meant to be logged or reported for debugging and resolution at the system level.

In contrast, exceptions (as opposed to errors) are meant to be caught, handled, and used for graceful error recovery within your Java applications.

--

--

Saurav Kumar

Experienced Software Engineer adept in Java, Spring Boot, Microservices, Kafka & Azure.