Java has a very powerful and robust mechanism to handle special, usually incorrect execution paths. The main purpose of this article is to help you to make most of that mechanism and to be aware of some known pitfalls.

General Guidelines:#

Do not waste useful information#

Java exceptions are usually quite informative and this information is usually quite valuable since exceptions are often associated with problems that should be fixed.

There are several common mistakes that you should try to avoid:

  • Do not silently ignore exceptions. The execution of catch blocks should have some visible effects at least to the log.
  • Do not create new type of exception just by using the getMessage() method. It is possible that the message is empty or even misleading if you have lost the exception type. Use class names at least.
  • Use the fillInStackTrace() method to re-throw exception if needed. Construction of a new exception is not only waste of memory and CPU time but it also leads to the loss of orginal stack trace.

Catch objects as specifically as possible#

Classes that can be thrown always inherit from Throwable but you must never say "catch (Throwable error)" in your code (but see the exceptions below). Although that extreme stupidity is fortunately rare, its more common form "catch (Exception error)" is a wide spread and time consuming problem among Java community. It is essential for a successful exception recovery that all catch implementations are so specific that they do not accept incorrect activations otherwise the system gets messy and very hard to maintain.

There are some special cases in which you actually want to catch all Exceptions or Throwables:

  • The software main error handler. You don't want your software to topple over and die in case something unexpected happens (such as a NullPointerException).
  • In the web.xml description file you probably want to set up a generic error page in case the compilation of your JSP page fails, because in that case the page-specific errorPage directive is not used.
  • If you are writing a specific utility package that needs to be robust. A good example is a logging package, which IMHO should never throw an exception.

--Janne

Never throw plain Exceptions#

It's very bad style to declare a method as throws Exception, because that forces the caller to always catch ALL Exceptions, including those he might have wanted to pass up to some more global error handler. Always use specific exception classes - in case there isn't one handy, make up your own.

Do not use RuntimeExceptions just because it is easy#

All methods are implicitely capable to throw RuntimeMethods like NullPointerExceptions and IllegalArgumentExceptions. Some people tend to use this shortcut to throw their own exceptions as RuntimeExceptions just because then they do not need any try-catch statements for the method calls.

RuntimeExceptions are exceptions that can be thrown during the normal operation of the Java Virtual Machine for example if there is something wrong with the input. Do not use these exceptions if the process itself fails.

Janne: However, if the input to your method is erroneus, I see no reason why you couldn't throw an IllegalArgumentException, for instance. However, it's good style to declare this in the method definition so that someone who uses your method won't be caught unaware.

Asser: Exactly :-) Incorrect input is the most common (only?) cause of runtime problems when the problem is not caused by a failure of VM environment or a software bug. Sorry, maybe I wasn't too clear above. After all RuntimeExceptions are not bad; they are something that should be used wisely.

Errors#

Errors are things that should never happen and it is usually hard to recover from them when they do. Catching Errors is always a step into unsafe territory but of course it makes sense sometimes. The thing is that you will have to be extremely careful with them since they are usually signs of problems in the environment.

Error handlers are typically some sort of smart shutdown routines producing some log output.

Original author: Asser


Items For Discussion:#

How would you handle exceptions on several levels in a complex application?#

Say, for example, that you have an app that uses JDBC for some custom purpose, catches JDBC exceptions, determines it has to pass the information upward, catches the exception again to modify behavior (e.g. failed authentication), passes it up, and finally catches it once more to display a happy message to the user.

It would be useful to have information on the full chain of events thus far at any step. This pretty much precludes the normal catch/throw new practice, and fillInStackTrace() won't be easy to examine programmatically.

Would a chainable exception type be useful, or can you point out an elegant pattern for handling this kind of situation?

-- ebu

Good question... It seems that chainable types are quite common in standard APIs for that kind of situations where the actual cause is behind several layers. For example such generic exceptions as java.sql.SQLException, javax.ejb.EJBException, javax.naming.NamingException, and javax.servlet.ServletException all use this pattern.

-- Asser

RMI also follows the same pattern. It is probably a good practice anyway to create your own Exception for each package/purpose/whatever, and make them chainable. --Janne

Bebbo: Only invent an own Exception type when it is really required. The examples provided here, are defining API's to be used by third parties. So it makes sense and results to better code if one API throws one Exception (or a distinct set of). You dont have to care about the zillions of different exceptions handled within that API.

Here some examples, where I do not invent own Exception types:

  • programming a library which is only used via RMI, i use RemoteException directly, since other Exceptions must be mapped anyway.
  • in private methods I often throw the original Exception type or a plain Exception, if the invoking method handles them fully.
--Bebbo

If you really want to have clean separation between layers, then nesting exceptions isn't a good way to go. Suppose the persistence layer throws a JDBC exception up to your business layer. Now the business layer knows how the persistence layer is implemented; you've broken your abstraction. Furthermore, if you're crossing classloaders or JVMs, now the business layer has a classloader-level dependency on the persistence layer implementation. Better to log the exception before it crosses layer boundaries and throw a more appropriate Exception subclass defined in the interface between layers.

Also, it's never good to throw plain Exception, it forces the client code to swallow any other unexpected Exceptions that might occur, instead of having it handled in a more appropriate location.

--KenLiu


Is it more efficient to check the input by using normal if-then-else statements than catching IllegalArgumentException?#

I mean, is there any extra overhead in catching exceptions than using if.. statements. What are the pros and cons in either cases?

-- Jarmo

My gut feeling is that if-then-else is cheaper than throwing an Exception. Constructing new objects is almost certainly more expensive than a simple if() -clause. Also, the JVM needs to build the error message (construct a new String), and fill in the stack trace, then forcifully exit your context, and look for the nearest exception handler.

However, the if() clause needs to be evaluated every time you call the method, whereas the Exception is constructed only very rarely. So, if you're planning to throw Exceptions a lot, then it can get expensive, but if your Exceptions are really just for error cases (see java.io.Reader for an example - the end of stream is denoted by the return value as opposed to an Exception), then you shouldn't probably bother with complicated if-then-elses. --Janne

Indeed, one Java optimization book I've read says that the time to create an Exception is 600 times longer than the time to run instanceof. The stack trace is very expensive to generate. -- MahlenMorris

The time to process an exception grows linearly with the depth of the stack trace due to the current way that exception handling is implemented. This can make frequent nested exceptions a big performance problem. But keep in mind that performance optimization information can grow stale very quickly with iterations of the JDK. -- MikeMusson

There is a good document on Java performance at IBM DeveloperWorks: Building High-Performance Applications and Servers in Java. It claims that using try-clause causes negliglible overhead until an Exception is created. --Janne

Bebbo: When discussing the processing speed, you should also consider when and how often the Exception would be raised.

If the Exception is an exception, use the Exception, otherwise use if/else.#

Maybe of topic, but if you want fast code, then also try to avoid else branch, use return, continue and break.

if (arg == 5) { return true; } // NO ELSE ... // more code return false;

--Bebbo

About catch (Exception ex)#

Any comments on this: http://www.javaworld.com/javaworld/jw-03-2001/jw-0323-badcode-2.html

The page is about making bad code good, general refactoring. The part I am asking for comments is the Exception-handling part, at the middle of the page. It advices to 'centralize' your Exception-handling, so that you don't have to write same handling over and over again, using method for it instead. Is it good or bad, should it be used somehow?

-- tuomasp

Add new attachment

Only authorized users are allowed to upload new attachments.

List of attachments

Kind Attachment Name Size Version Date Modified Author Change note
htm
Exception Categories.htm 81.8 kB 1 15-Mar-2005 11:40 203.187.209.229
png
attachment_big.png 0.8 kB 1 15-Mar-2005 11:42 203.187.209.229
« This page (revision-19) was last changed on 25-Aug-2006 14:27 by Candid Dauth