Things every Java developer must know about Exception handling

Exceptions are one of the most misunderstood (and misused) features of the Java programming language. This article describes the absolute minimum every Java developer must know about exceptions. It assumes that the reader is somewhat familiar with Java.

Historical Perspective

Back in the heyday of the “C” programming language, it was customary to return values such as -1 or NULL from functions to indicate errors. You can easily see why this isn’t a great idea – developers had to check and track possible return values and their meanings: a return value of 2 might indicate “host is down” error in library A, whereas in library B, it could mean “illegal filename”.

Attempts were made to standardize error checking by expecting functions to set a global variable with a defined value.

deleteme

James Gosling and other designers of the language felt that this approach would go against the design goals of Java. They wanted:

  1. a cleaner, robust and portable approach
  2. built in language support for error checking and handling.

Luckily, they didn’t have to look too far. The inspiration for handling errors came from a very fine language of the 60’s: LISP.

Exception Handling

So what is exception handling? It is unconventional but simple concept: if an error is encountered in a program, halt the normal execution and transfer control to a section specified by the programmer. Let’s look at an example:

try {
   f = new File("list.txt"); //Will cause an error if the file is not found...
   f.readLine;
   f.write("another item for the list");
   f.close();
} catch (FileNotFoundException fnfe) { // ... and transfer control to this section on error.
   // Do something with the error: notify user or try reading another location, etc

}

Exceptions are exceptional conditions that violate some kind of a “contract” during program execution. They can be thrown by the language itself (e.g. use a null reference where an object is required) or by the developers of program or API (e.g. passing date in British format instead of American). Some examples of exceptions are:

  • Accessing index outside the bounds of an array
  • Divide by 0
  • Programmer defined contract: Invalid SQL or JSON format

Exceptions disrupt the normal program flow. Instead of executing the next instruction in the sequence, the control is transferred to the Java Virtual Machine (JVM) which tries to find an appropriate exception handler in the program and transfer control to it (hence disrupting the normal program flow).

Checked and Unchecked Exceptions

Before we look at the exception classes in Java, let’s understand the two categories of exceptions in Java:

Checked exceptions – You must check and handle these in your program. For example, if you are using an API that has a method which declares that it could throw a checked exception, you must catch the exception each time you call that method. If you don’t, the compiler will notice and your program will not compile. The designers of the Java wanted to encourage developers to use checked exceptions in situations from which programs may wish to recover: for example, if the host is down, the program may wish to try another address.

Unchecked exceptions on the other hand are not required to be handled or caught in the program. For example, if a method could throw unchecked exceptions, the caller of the method is not required to handle or catch the exceptions.

Remember: Checked exceptions are mild and normally programs wish to recover. They must be caught and this rule is enforced by the compiler. The compiler doesn’t care whether you do or do not catch unchecked exceptions.

Many people find dichotomy between checked and unchecked exceptions confusing and counter-intuitive. Discussing the arguments from both sides are beyond the scope of this post.

Parent of all exception classes: Throwable

All exceptions in Java descend (subclass) from Throwable . It has two direct children:

  1. Exception
  2. Error

Error and its sub-classes are used  for serious errors from which programs are not expected to recover,  i.e. unchecked exception.

Exception and its sub-classes are used for mild errors from which programs may wish to recover, i.e. checked exception. Right? Well, there is a twist. There is just one sub-class which is different, that is, unlike it’s parent the Exception class, it is unchecked. It’s called the RuntimeException.

deleteme

 

Checked exception classes (mostly): Exception

Exception and its sub-classes must be caught and as such they force the programmer to think (and hopefully) deal with the situation. It is a signal that something didn’t go as intended along with some information about what went wrong, and that “someone” should do something about it. (e.g. car’s dashboard indicating that the battery needs service).

According to official documentation:

These are exceptional conditions that a well-written application should anticipate and recover from. For example, suppose an application prompts a user for an input file name,  [..] But sometimes the user supplies the name of a nonexistent file, and the constructor throws java.io.FileNotFoundException. A well-written program will catch this exception and notify the user of the mistake, possibly prompting for a corrected file name.

Source: The Java Tutorials

RuntimeException

RuntimeExceptions are used to indicate programming errors, most commonly violation of some established contract. They make it impossible to continue further execution.

For example, the contract says that the array index mustn’t go past [array_length – 1]. If you do it, bam, you get a RuntimeException. A real world analogy would be pumping diesel into a gasoline car: the unwritten contract says that you must not do it. There are no  signals, just the white smoke before the car comes to a grinding halt after a while. The message: it was your fault and could’ve been prevented by being smarter in the first place.

These are exceptional conditions that are internal to the application, and that the application usually cannot anticipate or recover from. These usually indicate programming bugs, such as logic errors or improper use of an API.

Source: The Java Tutorials

Error

These exceptional circumstances are like “act-of-god” events. Going back to our previous analogy, if a large scale alien invasion were to happen, there is nothing you could do your protect your car, or yourself (unless your last name is Ripley). In Software world, this amounts to the disk dying while you are in the process of reading a file from it. The bottom line is that you should not design your program to handle Errors since something has gone wrong in the grand scheme of things that are beyond your control.

These are exceptional conditions that are external to the application, and that the application usually cannot anticipate or recover from. For example, suppose that an application successfully opens a file for input, but is unable to read the file because of a hardware or system malfunction.

Source: The Java Tutorials

It’s not so black and white

Checked exceptions are often abused in Java. While Java forces developers to catch unchecked exceptions, it cannot force them to handle these exceptions. It’s not hard to find statements like this even in well written programs:

try {
   Object obj = ...
   Set<String> set = ...
   // perform set operations
} catch (Exception e) {
   // do nothing
}

Should you ever catch Runtime Exceptions?

What’s the point of catching RuntimeExceptions if the condition is irrecoverable? After all, if you were catching every possible run-time exception, your program will be cluttered with exception handling code everywhere.

RuntimeExceptions are rare errors that could be prevented by fixing your code in the first place. For example, dividing a number by 0 will generate a run time exception, ArithmeticException. But rather than catching the error, you could modify your program to check the arguments for division function and make sure that the denominator > 0. If it is not, we can halt further execution or even dare to throw a exception of our own: IllegalArgumentException.

In this case, the program got away by verifying the input parameters instead of catching RuntimeExceptions.

So when is it OK for an application to catch RuntimeExceptions?

A while back, I architected a high-performance traffic director with the goal of operating in the proximity of 10,000 transactions per seconds (TPS). The project had a very high availability criteria and one of the requirement was that it “must-never-exit”.

The director performs minimum amount of processing on each transaction before passing it further. Transactions came in two flavours, call them: A and B. We were only interested in transactions of type A. We had a transactions handler to process type A. Naturally, it “choked” run time exceptions when we passed in transactions of type B. The solution? Create a function and pass it every single transaction. If it returned true, we continued to further processing. Otherwise, we simply ignored the transaction, and continued onto the next one.

boolean checkFormat(Transaction t) {
//return true if the t is of type A. false otherwise.
}

This worked well, except…..

… the analysis showed that this function returned false only once a year. The reason, 99.99999999999999% transactions were of type A. Yet, we were subjecting every single transaction to be checked. This does not sound so bad, but due to the nature of transactions, the only way to differentiate was by doing expensive String comparison on various fields.

When this finding was brought to my knowledge, I immediately had the `checkFormat(…)` function removed and instead let the handler do it’s course and throw RuntimeException upon encountering transaction of type, B. When the exception gets thrown once a year, we catch it, log it and move onto the next transaction. The result: improvement in performance, and room to squeeze in additional calculations.

Summary

Exceptions in java are either checked or unchecked. Checked exceptions must be caught in the program otherwise the compiler will complain. While Java encourages developers to follow certain guidelines when it comes to exception handling, there aren’t any hard and fast rules and the rules are often bent.