Virtual Developer Workshop: Containerized Development with Docker
|Bruce Eckel's Thinking in Java||Contents | Prev | Next|
An exceptional condition is a problem that prevents the continuation of the method or scope that you’re in. It’s important to distinguish an exceptional condition from a normal problem, in which you have enough information in the current context to somehow cope with the difficulty. With an exceptional condition, you cannot continue processing because you don’t have the information necessary to deal with the problem in the current context . All you can do is jump out of the current context and relegate that problem to a higher context. This is what happens when you throw an exception.
A simple example is a divide. If you’re about to divide by zero, it’s worth checking to make sure you don’t go ahead and perform the divide. But what does it mean that the denominator is zero? Maybe you know, in the context of the problem you’re trying to solve in that particular method, how to deal with a zero denominator. But if it’s an unexpected value, you can’t deal with it and so must throw an exception rather than continuing along that path.
When you throw an exception, several things happen. First, the exception object is created in the same way that any Java object is created: on the heap, with new. Then the current path of execution (the one you couldn’t continue, remember) is stopped and the handle for the exception object is ejected from the current context. At this point the exception-handling mechanism takes over and begins to look for an appropriate place to continue executing the program. This appropriate place is the exception handler , whose job is to recover from the problem so the program can either try another tack or simply continue.
As a simple example of throwing an exception, consider an object handle called t. It’s possible that you might be passed a handle that hasn’t been initialized, so you might want to check before trying to call a method using that object handle. You can send information about the error into a larger context by creating an object representing your information and “throwing” it out of your current context. This is called throwing an exception . Here’s what it looks like:
if(t == null)
throw new NullPointerException();
This throws the exception, which allows you – in the current context – to abdicate responsibility for thinking about the issue further. It’s just magically handled somewhere else. Precisely where will be shown shortly.
Like any object in Java, you always create exceptions on the heap using new and a constructor gets called. There are two constructors in all the standard exceptions; the first is the default constructor, and the second takes a string argument so you can place pertinent information in the exception:
if(t == null)throw new NullPointerException("t = null");
This string can later be extracted using various methods, as will be shown later.
The keyword throw causes a number of relatively magical things to happen. First it executes the new-expression to create an object that isn’t there under normal program execution, and of course, the constructor is called for that object. Then the object is, in effect, “returned” from the method, even though that object type isn’t normally what the method is designed to return. A simplistic way to think about exception handling is as an alternate return mechanism, although you get into trouble if you take that analogy too far. You can also exit from ordinary scopes by throwing an exception. But a value is returned, and the method or scope exits.
Any similarity to an ordinary return from a method ends here, because where you return is someplace completely different from where you return for a normal method call. (You end up in an appropriate exception handler that might be miles away – many levels lower on the call stack – from where the exception was thrown.)
In addition, you can throw any type of Throwable object that you want. Typically, you’ll throw a different class of exception for each different type of error. The idea is to store the information in the exception object and in the type of exception object chosen, so someone in the bigger context can figure out what to do with your exception. (Often, the only information is the type of exception object, and nothing meaningful is stored within the exception object.)