Error Handling in Java

 

Just like in every programming language, during runtime, a Java program might be disrupted by abnormalities caused by factors like user input, system malfunction, network problems etc. Such abnormalities disrupt the normal program flow and cause termination of the program if not handled properly. An exception is an event that represents an abnormality that occurs when a Java program is executed. For example, trying to parse a Number from a String that does not represent a number causes a NumberFormatException. When this happens, if the exception is not handled, program flow stops and exits. Exception objects are wrappers around such abnormalities. They store information about the details about the kind and the cause of the problem and present it to the developer in an understandable way when the exception occurs. Using Exception objects, the abnormalities in the program can be handled in a way not to disrupt the program flow or to give information to the user/developer about the cause of the issue.

Most typically, when a problem occurs during the execution of instructions inside a method, an exception object is created and this object is picked up by the Java runtime. This event is called throwing an exception. At that moment, Java runtime starts searching for instructions defined to handle this specific exception thrown. The set of instructions written to handle an exception is called an exception handler. For that, the runtime looks into the list of the methods by order that have been invoked before the method which threw the exception. This list of methods is called the call stack of the exception. An exception is handled by catching the exception in a catch block.

Let’s say we have a class one of which methods throws an exception in its body.

public class MyClass {

public void a() {

b();

}

public void b() {

c();

}

public void c() {// exception is thrown in this method }

public static void main(String [] args) {

MyClass myClass = new MyClass();

myClass.a();

}

}

Here, the main method calls method a() of MyClass through myClass reference variable. In return, method b() is called by a(), method c() is called by b() and then an exception is thrown inside method c(). In this case, the call stack for the exception thrown inside method c() is main → a → b → c.

An exception handler handles the exception that was thrown by catching it. This feature is known as catching an exception. If Java runtime cannot find an exception handler in the call stack, the program exits with an error code.

Exceptions are defined for specific error conditions. Using exceptions to handle abnormalities in the code makes the code more readable and makes it easier to manage program errors.

Exceptions Types

java.lang.Throwable class is the parent of all exceptions and errors in Java. A Java exception can be defined simply by defining a class that extends java.lang.Throwable. Throwable class has two direct subclasses that are built into Java:

  • java.lang.Exception
  • java.lang.Error

In practical, ordinary Java programs don’t throw errors. The JVM throws an error when there’s an error related to the runtime environment during program execution.

java.lang.Exception is the generic exception class which can be simply initialized using the no-arg constructor or one of its other constructors. All the exception classes that come with Java are subclasses of the Exception class. Because of this, it covers all the possible error scenarios that may occur while executing non-custom Java code.

There are three types of exceptions in Java:

1- Errors: Errors correspond to abnormalities caused by factors other than the ones that can occur during the execution of statements inside the code, like a system hardware failure. Catching such exceptions is not required by the runtime system but if a developer thinks this is a real possibility, he/she might write the code to catch and handle it.

Errors extends java.lang.Error class.

2- Checked exceptions: These are exceptions that require explicit handling in the code if a method that declares to throw them is invoked. For example, if a method that opens a network connection calls a method from java.net package that throws java.net.ConnectException, it is required to handle the exception in an explicit way. Similarly, calling the sleep() method while a Java thread runs requires handling java.lang.InterruptedException explicitly because it is a possibility that the sleeping condition might be interrupted, making this is a checked exception. Failing to handle checked exceptions in code results in a compile error.

3- Runtime Exceptions: Runtime exceptions are typically caused by bugs in the program and do not require explicit handling. Therefore, not handling a runtime exception in the code does not cause a compile error. For example, division by zero operation with integer operands, such as 1 / 0, throws a java.lang.ArithmeticException which is a runtime exception. This is a condition that can be handled in the code itself and can be prevented by having control over user input. Therefore, Java code that uses integer division is not required to handle division by zero case explicitly. Runtime exceptions extend java.lang.RuntimeException class which is a subclass of java.lang.Exception. They are also called unchecked exceptions.

Any Java class that is a subclass of java.lang.Throwable or java.lang.Exception but not a subclass of java.lang.RuntimeException is a checked exception.

What is catch or specify requirement?

If a Java method throws a checked exception, any method calling this method should handle the exception with a try/catch block or declare that it throws the same exception. This requirement is known as catch or specify requirement.

A method declares that it throws a checked exception using the throws keyword. The format for declaring throwing an exception as follows:

<modifiers> <return type> <method name>(<arguments>) throws <checked exception>

For example, given that MyException is a checked exception, a method may declare that it throws MyException as follows:

public void myMethod() throws MyException {

}

Any method that calls myMethod() is required to do one of the following:

1- Either declare that it throws MyException

public void callerMethod() throws MyException {

myMethod();

}

2- or handle it in a try/catch block.

public void callerMethod() throws MyException {

try {

myMethod();

} catch (MyException myException) {

//code to handle the exception

}

}

try/catch blocks

In Java language, exceptions are handled using try/catch blocks. A try block surrounds the instructions that throws an exception or multiple exceptions. The catch block(s) follows the try block and catches the exception(s) thrown by the statements inside the try block. Each catch block catches a specific exception and handles it inside a separate block. For example if a method named myMethod() throws two checked exceptions as follows,

public void myMethod(…) throws MyException1, MyException2 { …}

the method calling myMethod() should surround it with a try block and each catch block following this try block should handle one of those exceptions explicitly as follows:

public void callerMethod(…) {

try {

myMethod(…);

} catch (MyException1 e) {

//Code to handle MyException1.

} catch (MyException2 e) {

//Code to handle MyException2.

}

}

Runtime exceptions can be handled with try/catch blocks, too. Let’s say, in the example above, myMethod() is likely to throw a Runtime exception named MyRuntimeException. The try/catch block might catch this exception, too, if the developer wanted so.

try {

myMethod(…);

} catch (MyRuntimeException e) {

//Code to handle MyRuntimeException.

} catch (MyException1 e) {

//Code to handle MyException1.

} catch (MyException2 e) {

//Code to handle MyException2.

}

}

Inside the try block, if a statement throws an exception that is caught by one of the catch blocks, the program execution exits the try block and the remaining lines inside the block are skipped if there are any. Following that, Java runtime first checks the first catch block. If the first catch block does not catch the exception thrown, then it checks the second one, and so on, until the catch block declared to catch the exception is reached. If none of the catch blocks catch the exception, the program flow exits with an error code. Once the exception thrown is caught, the program execution continues with the first line of the catch block. Following that, the flow runs through the catch block, given that no exceptions occur inside the catch block. Once all the instructions inside the catch block are executed, the program flow continues with the first line following all the catch blocks. Only one of the catch blocks surrounding a try block is executed and all the others are skipped.

The order of the catch blocks is important because the program flow follows the catch blocks sequentially. The order of catch blocks should be such that more specific exceptions are caught before more general exceptions. A subclass of an exception is referred to as being more specific than its parent exception. If the catch block for a less specific exception comes before the one for a more specific exception, it catches the exception before the more specific one does. Therefore, the catch block for the more specific exception becomes unreachable, causing a compile error.

For example, let’s consider the following case.

try {

Integer.parseInt(“a”);

} catch (Exception e) {

}

catch (NumberFormatException e) {

}

Here, a NumberFormatException is thrown while trying to parse “a” into an integer. This exception is caught by the first catch block because NumberFormatException is a subclass of Exception. This makes the second catch block unreachable though it catches the exact exception thrown inside the try block. This causes a compile error. The right order should be

try {

Integer.parseInt(“a”);

}

catch (NumberFormatException e) {

}

catch (Exception e) {

}

Creating Exceptions

The easiest want to create a checked exception is to extend java.lang.Throwable or java.lang.Exception. Throwable is also the immediate superclass of Error, so extending Throwable to create an Exception class would mean a more general thing and is not recommended. It is a good practice to create exception classes that handle exceptions for specific cases, like division by zero. We will follow this approach while creating new exceptions.

An unchecked exception can be created by extending java.lang.RuntimeException class.

Any class extending a checked exception is a checked exception and any class extending an unchecked exception is an unchecked exception.

Now let’s define a class with a method that performs division operation on integers.

public class MyClass {

public double division(int a, int b) {

return a / b;

}

}

For most numbers this method would work without any problems. However, if the second argument is 0, the method would have a problem executing the statement a / b, because division by 0 is not an operation defined for integers. Unhandled, JVM would throw a java.lang.ArithmeticException if this method performed division by 0 operation and the program would terminate itself. The output of the terminated program would contain text that would be some form of the following message:

Exception in thread “main” java.lang.ArithmeticException: / by zero

Instead of leaving it up to JVM, an Exception class can be created to give a meaningful message to the class which calls this method. Let’s define an Exception class for this purpose. We can do this by either extending Throwable or Exception.

public class DivisionByZeroException extends Exception {

public DivisionByZeroException(String errorMessage) {

super(errorMessage);

}

}

Now, the division method can be declared to throw a DivisionByZeroException exception. This is done using the throws keyword.

public class MyClass {

public int division(int a, int b) throws DivisionByZeroException {

try {

return a / b;

} catch (ArithmeticException e) {

throw new DivisionByZeroException(“Division by 0 is not allowed for integers.”);

}

}

}

In division() method, we catch the runtime Exception, ArithmeticException, and inside the catch block we throw a DivisionByZeroException with our custom error message. DivisionByZeroException extends Exception, therefore it is a checked exception. So any method throwing DivisionByZeroException needs to honor catch or specify requirement. In this example, our preference is to specify it with the throws clause for division() method since we want tell the methods that invoke division() that it may throw this exception.

Now, let’s add a new method to our class that calls division() method.

public class MyClass {

public int division(int a, int b) throws Exception {

return a / b;

}

public void printDivisionResult(int a, int b) throws DivisionByZeroException{

System.out.println(a + “ divided by “ + b “ + “ is “ + (a / b));

}

public static void main(String [] args) {

MyClass myClass = new MyClass();

try {

myClass.printDivisionResult(4, 0);

System.out.println(“Result printed on the console.”);

} catch (DivisionByZeroException e) {

System.out.println(“Division by zero is not allowed. ”);

}

System.out.println(“The program execution is finished.);

}

}

The output would be

Division by zero is not allowed.

The program execution is finished.

Here, in this example, printDivisionResult method calls division method and it honors catch or specify by throwing the exception itself, too. In this case, main method which calls printDivisionResult is required to honor catch or specify. main() method does this by catching the exception, printing a meaningful message and exiting with an error code. Clearly, while handling such cases, using the specific DivisionByZeroException gives much more information about the cause of the problem than Java’s default ArithmeticException. Because DivisionByZeroException exception is thrown on line 12, “Result printed on the console.” text is never written on the console. Once the exception is thrown, the program flow moves to line 15, the beginning of the catch block that handles the exception. The code in the catch block is executed and the program flow moves to line 16, the first line after the catch block. Following this, “The program execution is finished.” text is printed on the console.

If printDivisionResult method was called with arguments that do not cause a DivisionByZeroException, then the catch block would not be executed. For example, if the method was invoked as printDivisionResult(4, 2), the output would be

2

The program execution is finished.

Alternatively, in this example, main() method may honor catch or specify by declaring that it throws a DivisionByZeroException or by declaring that it throws an Exception which is a parent of DivisionByZeroException.

//Specifying DivisionByZeroException

public static void main(String [] args) throws DivisionByZeroException {

//Specifying Exception

public static void main(String [] args) throws Exception {

This would mean that, if the division by zero condition occurred, the problem would be handled by the JVM, as the main method is the entrance of the JVM to the program.

finally blocks

Optionally, a try/catch block can be followed by a finally block. The finally block contains the code that is guaranteed to execute following a try/catch block. This code is executed even if an exception happens in the catch block. Only if the catch block has a return statement, then the code in the finally block is not executed. Finally blocks are especially useful while running code to free up resources if an unexpected exception occurs.

A try/catch/finally block is declared as follows:

try {

//exception throwing code

} catch (MyException e) {

//exception handling code

} finally {

//the code that is executed following the catch block

}

Java also allows having a finally block following a try block, without a catch block. Because finally blocks are guaranteed to execute, the instructions inside the finally block are executed even if an exception is thrown inside the try block.

Now, let’s change our example a little bit to introduce a finally block.

public class MyClass {

public int division(int a, int b) throws DivisionByZeroException {

return a / b;

}

public void printDivisionResult(int a, int b) {

try {

System.out.println(a + “ divided by “ + b “ + “ is “ + (a / b));

System.out.println(“Result printed on the console.”);

} catch (DivisionByZeroException e) {

System.out.println(“Division by zero is not allowed. ”);

} finally {

System.out.println(“Executing the finally block.”);

}

}

public static void main(String [] args) {

MyClass myClass = new MyClass();

myClass.printDivisionResult(4, 0);

System.out.println(“The program execution is finished.);

}

}

The output would be

Division by zero is not allowed.

Executing the finally block.

The program execution is finished.

This time we moved the try/catch block into the printDivisionResult method. printDivisionResult method this time honors catch or specify by catching the exception in a try/catch/finally block. The exception occurs on line 8, and then the code in the catch block is executed and this is followed by the code in the finally block. After the finally block, the program flow moves to line 20, which is the first line after the method exits.

Now, let’s remove the catch block from this class and have a try/finally block without a catch block.

public class MyClass {

public int division(int a, int b) throws DivisionByZeroException {

return a / b;

}

public void printDivisionResult(int a, int b) {

try {

System.out.println(a + “ divided by “ + b “ + “ is “ + (a / b));

System.out.println(“Result printed on the console.”);

} finally {

System.out.println(“Executing the finally block.”);

}

}

public static void main(String [] args) {

MyClass myClass = new MyClass();

myClass.printDivisionResult(4, 0);

System.out.println(“The program execution is finished.);

}

}

Running this class produces the output

Executing the finally block.

The program execution is finished.

From the output we see that even though we did not have a catch block, the finally block was still executed after the exception was thrown.

Printing the stack trace

Printing the call stack of an exception when it is caught is a methodology that is frequently used by developers. Exception class defines two important methods to get information about an exception:

– getMessage: Returns the message explaining the exception

– printStackTrace: Returns the call stack of the exception

Now, let’s change out previous example to print the stack trace on the console.

public class MyClass {

public int division(int a, int b) throws DivisionByZeroException {

return a / b;

}

public void printDivisionResult(int a, int b) {

try {

System.out.println(a + “ divided by “ + b “ + “ is “ + (a / b));

System.out.println(“Result printed on the console.”);

} catch (DivisionByZeroException e) {

e.printStackTrace();

}

}

public static void main(String [] args) {

MyClass myClass = new MyClass();

myClass.printDivisionResult(4, 0);

System.out.println(“The program execution is finished.);

}

}

We would see the stack trace on the printed on the console.

%d bloggers like this: