Share on Twitter
Share on Facebook
Share on HackerNews
Share on LinkedIn

Exception Handling in Java (with Real Examples)

Java has been one of the most widely used programming languages among developers worldwide for years. So naturally, it is a popular choice for those beginning their careers in development.

Learning Java requires more than just knowing the proper syntax and effective code hygiene. Any developer who hopes to use Java for commercial development must be able to quickly and competently identify and recognize errors in their code.

While the features of code editors go a long way toward pointing out basic programming errors, other issues are more easily identified during program compilation or in runtime environments. And one of the primary tools developers have for addressing anomalous conditions at runtime is exception handling.

Both novice and more experienced programmers should understand how to use exception handling to improve their code. This article discusses the basics of exception handling in Java and how Sentry can help make exception handling simple and more powerful.

What is an exception?

Formally, an exception in Java is “an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions.” There are many typical causes for exceptions in Java, including:

  • Loss of network connectivity
  • Invalid input data
  • Requests for missing or non-existent files
  • Exceeding memory limits for the Java Virtual Machine (JVM)
  • Code errors

Officially, exceptions are distinct from errors because errors are more serious issues that the application “should not try to catch.” Most developers, however, consider errors to be just a subset of exceptions.

What happens when a program method throws an exception?

Errors encountered during runtime create exception objects containing basic information about the exception, including the type of exception generated and the system’s state at the time of the exception. The method then throws the exception to the runtime system for processing.

After receiving an exception, the runtime system tries to find a way to resolve it. Tracing backward through the call stack from the method where the exception occurred, the runtime system looks for an existing method that can process the exception. These methods are called exception handlers. If no method in the call stack can handle the exception, both the runtime system and the application stop.

Why is exception handling important?

Exception handling is crucial for the proper functioning of your applications. With exception handling, developers define the steps for addressing compilation or runtime errors to ensure that there are no interruptions of program flow. As a result, applications are more stable and have a better user experience. And because coding errors that lead to exceptions can be points of vulnerability for cyberattacks, effective exception handling can lead to more secure code.

What types of exceptions exist in Java?

To best understand how to use exceptions and exception handling, you must be familiar with the types of exceptions built into Java.

Java’s built-in exceptions fall into two broad categories: checked and unchecked exceptions. Checked exceptions are compile-time rather than runtime exceptions. During compilation, the compiler specifically looks for checked exceptions and whether the program includes exception handlers. If no appropriate exception handler exists for a checked exception, the compiler generates a compilation error.

In contrast, unchecked exceptions are runtime exceptions, and the compiler will generally ignore them.

More advanced Java programmers can also create custom exceptions to gain even deeper insight into the functioning of their code. Custom exceptions can be tremendously useful for pinpointing issues with specific business logic that the built-in Java exceptions would not catch, helping your business build the best possible reputation for its products.

What is the hierarchy of exception classes?

Java establishes a hierarchy for exceptions under the Throwable class. The first level of the class divides issues into exceptions and errors. The exceptions hierarchy is broadly divided by exception type. Specifically, the broadest subclasses of exceptions are:

  • IOException
  • ReflectiveOperationException
  • CloneNotSupportedException
  • InterruptedException
  • RuntimeException

All RuntimeExceptions are unchecked exceptions, while all remaining exceptions are checked.

java-exception-hierarchy

Common examples of handling exceptions in Java

While the complete list of built-in exceptions is extensive, a few are more frequently encountered than others.

Common checked exceptions

Many checked exceptions arise when the program cannot find items it expects to exist. These exceptions use phrases like NotFound or NoSuch in their name and are part of the ReflectiveOperationException subclass.

ClassNotFoundException

This is one of the most common exceptions in Java. There are several situations where a ClassNotFound exception can occur, including:

  • Java Classloader or Class.forName() methods cannot find a specified class in the classpath when attempting to load the class.

  • Attempting to load Java database connectivity (JDBC) files using Class.forName() if the jar file is not in the class’s path.

It is simple to set up a test of the ClassNotFound exception using a try-catch block. The try-catch block is specifically intended to identify potential exceptions. The try statement encloses potentially problematic code, and the catch defines the exception for which you are testing and performs any actions the developer wants to occur in the event of an exception. Importantly, rather than terminating when the exception occurs, the program continues to run.

public class ExceptionTest {

    public static void main(String args[])
    {

        try {

            Class.forName("FindMissingClass");
        }

        catch (ClassNotFoundException ex) {

            ex.printStackTrace();
        }
    }
}

When this code runs, the compiler outputs a ClassNotFound exception:

java-compiler-class-not-found-exception

Another example of this type of reflection exception is the FileNotFoundException, which can occur when a file is absent, or the application does not have sufficient permission to access it.

Resolving the ClassNotFound exception involves reviewing your classpath to ensure that all necessary classes and jar files are appropriately identified.

NoSuchMethodException

Not surprisingly, Java throws a NoSuchMethod exception when attempting to call a non-existent class method or a private method. A simple example shows how this exception can arise when a developer is not careful about defining and using method names.

class ExceptionTest
{
    public void method1(String firstString)
    {
        System.out.println(firstString)
    }
}

public class BadMethodCall
{
    public static void main(string[] args)
    {
        ExceptionTest obj = new ExceptionTest();
        obj.method1("This is the first method");
        obj.method2("This is the missiong method");
    }
}

Note that we did not use a try-catch block in this example. Unlike ClassNotFoundException, NoSuchMethodException can not be thrown in a try-catch block.

During compilation, you will get a missing method error, although it will show differently than with ClassNotFoundException:

java-missing-method-error

Interrupted Exception

Java throws an InterruptedException if a thread that is either occupied or not currently active (e.g., sleeping or waiting) is interrupted. You can create an interruption to show how this exception works:

class InterruptedThread extends Thread {

    public void run() {
        try {
            Thread.sleep(100);
        }
        catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class InterruptedExceptionTest {

    public static void main(String[] args) {
        InterruptedThread sleepingthread = new InterruptedThread();
        sleepingthread.start();
        sleepingthread.interrupt();
    }
}

After creating the InterruptedThread class, starting a thread, making it sleep for 100 ms, and then introducing an interrupt, Java throws an InterruptedException:

java-interrupted-exception

InvocationTargetException

This exception is actually an indicator of an underlying exception in a method. Java throws the exception when an attempt is made to invoke a method that throws the primary exception. InvocationTargetException does not provide specifics on the underlying exception, although developers can use the getCause() method for further information.

Common unchecked exceptions

Recall that unchecked examples only arise at runtime rather than appearing during compilation.

IllegalArgumentException

An illegal argument exception is thrown when input to a method is of the wrong type; for example, an array is passed when the method accepts only integer input or if the input is out of a specified input range. You can use the throw exception statement or try-catch blocks to create error messages if a user submits improper input data.

Similar exceptions exist for various other conditions, such as ArrayIndexOutofBoundException or NumberFormatException.

ArithmeticException

As its name implies, Java throws this exception if the program attempts an arithmetic operation that is inconsistent with Java operations. The most common example is dividing by zero.

public class ExceptionTest {

  public static void main(String[] args) {
    try {
        int divbyzero = 1/0;
    }
    catch (ArithmeticException ex) {
        ex.printStackTrace();
        System.out.println("ERROR: Divide by zero");
    }
  }
}

As you can see, the program not only prints out the stack trace to show the exception but also outputs an error message for the user:

java-arithmetic-exception

These are just a few of the built-in exceptions that developers can use to control application flow, inform users and themselves when there are issues in the code, and build more robust and stable applications. For more on exceptions, see the Java online documentation.

How Sentry can help monitor and resolve exceptions

Your compiler and runtime environments provide basic information on exceptions, but it can be unhelpful. Products like Sentry provide greater visibility into your code and exceptions, making it simple for you to quickly and easily see all unhandled exceptions in a way that allows you to prioritize your correction efforts easily.

java-events-capture

Sentry has several features that make it easier for you to understand what your exceptions are, how they are related, and how they tie to other components like APIs. It organizes similar exceptions into issues, minimizing the time needed to review and triage exceptions, and has built-in algorithms for grouping issues which developers can also define using their own grouping criteria.

With its distributed tracing methods, Sentry also monitors performance and highlights each issue’s impact on users, including how exceptions impact application latency. And using TraceView, Sentry provides more extensive information about each exception.

While knowing what features are available is useful, seeing Sentry in operation brings its issue handling benefits to life. For a particular issue, Sentry shows you how frequently it is occurring and how many users it impacts, making it easier for you to quickly prioritize your remediation efforts.

java-exception-handling-image17

You can then hover over an issue to get more specifics about what parts of the code give rise to the exceptions.

java-exception-handling-image6

And for even greater detail, you can follow the dropdown next to each specific code item to see the code itself.

java-exception-handling-image10

In addition, with the breadcrumbs feature, developers can visualize a detailed timeline of the events that occurred before a specific error.

java-exception-handling-image12

As with other Sentry features, you can customize breadcrumbs to meet your own needs and debugging tactics.

Sentry knows that you may have sensitive data that you must protect at all times (e.g., user credentials). They apply advanced data scrubbing algorithms to redact sensitive information from your data sources. You can also define and implement your own data scrubbing methods.

Sentry also monitors performance and highlights each issue’s impact on users, including how exceptions impact application latency. Sentry’s Performance Monitoring features give you insight into the success or failure of transactions and how they are impacting your users.

java-exception-handling-image2

You can drill down through transactions to see the users involved and the individual transaction traces for each individual. And for each trace, you can drill down even further to give you even greater visibility.

java-exception-handling-image16

Even the most advanced tool will fail if it isn’t simple to use and understand. So Sentry provides a rich set of easily comprehensible, customizable overview dashboards so you can immediately assess the health of your application. With easily added dashboard widgets, you can build a view that focuses on your primary issues of interest.

java-exception-handling-image3

It is obvious how Sentry simplifies the developer’s task of tracing and resolving issues and exceptions.

These are only a few of the many features that make Sentry a must-have addition to the development toolbox for Java developers serious about exception handling. For more detailed information, check out their documentation or signup to get started.

Your code is broken. Let's Fix it.
Get Started

More from the Sentry blog

ChangelogCodecovDashboardsDiscoverDogfooding ChroniclesEcosystemError MonitoringEventsGuest PostsMobileOpen SourcePerformance MonitoringRelease HealthResourceSDK UpdatesSentry
© 2024 • Sentry is a registered Trademark
of Functional Software, Inc.