Exceptions in Java: Finding and Fixing

Most Common Java Exceptions

When building custom applications in Java, exceptions in Java occur frequently. This can happen due to various reasons – for example, a user input contains an error, an external system that does not respond, or simply a programming error. However, like most modern programming languages, Java offers advanced exception-handling features that allow programmers to handle both errors and exceptions. But detecting exceptions in production and fixing the ones that impact performance and user experience is a hard problem.


Overview of Exceptions in Java

What is a Java Exception?

An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program’s instructions. An exception can occur for many reasons, some of which are invalid user input, coding errors, opening an unavailable file, loss of network connection, etc.

Types of Exceptions in Java

Generally, there are two kinds of Java exceptions:

    • Checked exceptions (compile-time exceptions)
    • Unchecked exceptions (runtime exceptions)

Common Terminology of Java Exception Handling

Call Stack

The call stack is a data structure that JVM uses to keep track of the method calls. The call stack is made up of stack frames, one for each method call. When the execution enters a method, a new frame is created and pushed onto the call stack. Similarly, when the execution exits a method, its frame is popped from the call stack. A stack frame usually stores local variables and arguments passed into the method and some metadata.

Exception Class and Hierarchy

The exception class identifies the type of event that occurred. Exception classes are part of the inheritance hierarchy, just like every other Java class. Exceptions must extend java.lang.Exception or one of its subclasses. By extending the Exception class or any of its subclasses, you can create your own exception classes.

Throwing an Exception

When an exception occurs within a method, the method creates an exception object and hands it off to the runtime system. This is called “throwing an exception” and is done using the keyword “throw”. The exception object contains information about the event, including its type and the state of the program when the event occurred. An exception object is a member of an exception class. Exceptions can be caught and handled by the program.

Exception Handling

After a method throws an exception, the runtime system attempts to find something to handle it. The set of possible “somethings” to handle the exception is the ordered list of methods that had been called to get to the method where the exception occurred. The list of methods is known as the call stack (see the next figure).

Exception Call Stack
The Call Stack

The runtime system searches the call stack for a method that contains a block of code that can handle the exception. This block of code is called an exception handler. The search begins with the method in which the exception occurred and proceeds through the call stack in the reverse order in which the methods were called. When an appropriate handler is found, the runtime system passes the exception to the handler. An exception handler is considered appropriate if the type of the exception object thrown matches the type that can be handled by the handler.

The exception handler chosen is said to catch the exception. If the runtime system exhaustively searches all the methods on the call stack without finding an appropriate exception handler, as shown in the next figure, the runtime system (and, consequently, the program) terminates.

Searching the call stack for the exception handler.
Searching the call stack for the exception handler.

Also Read: Null Pointer Exception In Java – Explained | How To Avoid and Fix

Checked and Unchecked Exceptions in Java

Java supports checked and unchecked exceptions.

  • Checked Exception

The classes that directly inherit the Throwable class except RuntimeException and Error are known as checked exceptions. For example, IOException, SQLException, FileNotFoundException, etc. Checked exceptions are called compile-time exceptions because these exceptions are checked at compile-time by the compiler. If a code within a method throws a checked exception, then it should either be handled by the method or specify it using the throws keyword.

  • Unchecked Exception

The classes that inherit the RuntimeException are known as unchecked exceptions. For example, ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException, NumberFormatException, etc. Unchecked exceptions (also known as runtime exceptions) are not checked at compile-time, but they are checked at runtime. Though a runtime exception is not required to be handled it does not mean that you do not need to be concerned about it.

How to Handle an Exception?

Handling Java exceptions happens with the try, catch, and finally blocks. They can be used to define how to handle exceptions when they occur.

Try blocks should include code that might throw an exception. If your code throws more than one exception, you can choose whether to:

  • Use a separate try block for every statement that may throw an exception or
  • Use one try block for several statements that could throw multiple exceptions

There is no limit to adding catch blocks, as you can add as many catch blocks as you want. However, you can add only one finally block to each try block.

Each catch block should handle exceptions thrown by the try blocks. The finally block always executes whether or not an exception is thrown after the successful execution of the try block or after one of the catch blocks.

After the try-catch block, programmers usually use finally block to do cleanup.

How to Specify an Exception?

If you don’t handle an exception within a method, it will propagate in the call stack. For checked exceptions, you need to either handle it or specify that the method might throw it. If you want to throw it, add a throws clause to the method declaration to accomplish this. As a result, all calling methods must handle or specify the exception themselves. Similarly, if you wish to specify that a method might throw an unchecked exception, you may do so.

public void doSomething(String input) throws MyBusinessException {
    // do something useful ...
    // if it fails
    throw new MyBusinessException("A message that describes the error.");

Knowing whether to Handle or Specify an Exception

It usually depends on the use case whether you should handle or specify a Java exception. As you might expect, it is difficult to provide a recommendation that works for all use cases.

For this, you can generally ask yourself the following questions:

  • Can you handle the exception within your current method?
  • Can you anticipate the needs of all your class members? And would handling the exception fulfill these needs?

If the answer to both questions is “yes”, you should handle the exception within your current method. In all other situations, it’s best to specify it. This allows the caller of your class to implement the handling according to the current use case.


Finding and Fixing Exceptions in Java with Seagence

The purpose of exceptions and exception handling is to try and recover the application from abnormal events. In case recovery is not possible, the application should notify the user and log the exception to the log file. When a defect occurs, the logged exceptions help developers quickly debug and detect the root cause to avoid business impact. Production applications throw numerous exceptions during runtime. Because all exceptions are not logged in the log file, debugging becomes a painful job. This job becomes even more painful when the exception causing the defect is swallowed or not logged in the log file. Sometimes defect-causing exceptions leave no trace in the log file if the proper logging level is not enabled in production.

Java Defects Automatically Found in Seagence with Root Cause

Automatically Found Exceptions in Java with Root Cause

Seagence automatically helps you find and fix exceptions in Java. Seagence (a Defect Monitoring Product) proactively detects known and unknown defects using a unique approach including root-cause in real-time, and eliminates the need for debugging. See the above figure with the Seagence detected list of defective transactions and list of exceptions thrown during the expanded transaction, and the root cause exception causing the defect. Seagence records all exceptions (handled, unhandled, and swallowed), errors thrown by the application, and indexes them for easy searching, so you lose no detail if debugging becomes necessary. With the Seagence detected defects and root-causes in hand, you can quickly fix your broken code with greater speed and accuracy. Click here to know more or try Seagence free here.