Introduction to Exceptions in Java
Life without exceptions
In languages that do not support exceptions, such a C, a method (or function) would usually indicate an error by returning an error code (usually an integer), which the calling code would then have to check. Or the method might return a boolean; true to indicate success, false to indicate failure. Or the method may return null to indicate failure, and a valid value to indicate success. Or an auxillary method (such as checkError()) may need to be called after the first to check for success or failure. Clearly, languages lack a consistent way to indicate and deal with errors.
Checking the error code from one method might be easy enough, but scale this to several method calls and it becomes a hassle. Consider the following pseudocode, a portion of a game, which reads the high score from a file and writes the new high score back to the file with the players name if the current score is higher.
updateHighScore(currentScore, playerName)
{
open high scores file;
read high score;
if (currentScore > high score)
{
write high score with playerName;
}
close file;
}
This code neglects to check for several possible error conditions. For example, the high score file may not exist, or it may not be readable. Writing to the file might also fail if the user does not have adequate permissions or if the disk runs out of space. With proper error handling, the code would look like this:
int performOperation()
{
errorCode = 0;
open high score file;
if (openSuccessful)
{
read high score;
if (readSuccessful)
{
if (currentScore > high score)
{
write high score with playerName;
if (writeFailed)
{
errorCode = -3;
}
}
}
else
{
errorCode = -2;
}
close file;
if (closeFailed)
{
errorCode = -4;
}
}
else
{
errorCode = -1;
}
return errorCode;
}
With error checking in place, the method has more than doubled in terms of lines of code. And all that error checking code obscures the normal flow of the program logic. The code is tedious to write, and difficult to read and maintain.
Sooner or later, programmers get so fed up with the tedium of having to check error conditions after each method call that they simply ignore error conditions. But ignoring error condtions leads to unpredictable behavior. For example, what happens to the program if we try to read a file that could not be opened? The program will likely crash, often leaving little clue about what went wrong, or where.
Enter Exceptions
Exceptions provide a consistent way to report and handle error conditions, and do so in a way that allows for robust code without placing undue burden on the programmer. Error codes and other C-style methods of error handling have shown us that if the error handling techniques provided by a language are difficult to use, they'll go unused. The consistency and ease-of-use provided by exceptions mean that programs written in languages like Java are often more robust and easier to debug.
An exception is an event that disrupts the normal flow of a running program. Though the terms exception and error are often used interchangeably, they aren't strictly synonyms. Not every error should be handled by raising an exception. As an example, if a user interface expects a positive integer, the program should not throw an exception if the user enters an invalid number. Instead, it should simply alert the user of the error in an appropriate manner. This subject will be discussed further in a future article.
Exceptions in Java
As you might expect, exceptions are objects in Java. Java provides several exception classes, and you may create your own subclasses to define application specific exception conditions.
The basic exception classes in Java are defined in the java.lang package. At the root is the Throwable class. Error and Exception each extend Throwable, and RuntimeException extends Exception. Figure 1 shows this hierarchy. Despite the nomenclature, each class in the hierarchy is generically referred to as an exception, with a lowercase 'e'.

Figure 1. Java exception hierarchy
Error and its subclasses represent non-recoverable conditions. Common examples include OutOfMemoryError and NoClassDefFoundError. The former indicates that the Java Virtual Machine ran out of memory and could not reclaim additional memory through garbage collection, and the latter indicates that a required Java class is not in the class path. Since there is usually nothing an application can do to recover from an Error, thse exceptions should not be caught. Instead, the JVM should be allowed to handle the Error, which usually means it will print a stack trace, and then terminate. While this may hardly sound ideal, Errors are rare in practice, and since they are by definition non-recoverable, terminating is the reasonable response.
On the other hand, Exception and RuntimeException and their subclasses represent error conditions that are recoverable. Exception and its subclasses (other than RuntimeException) are called checked exceptions, while RuntimeException and its subclasses are unchecked exceptions.
The most obvious difference between checked and unchecked exceptions is how the compiler deals with them. If a block of code can throw a checked exception, the exception must either be caught or declared in the throws clause of the method (or constructor). If not, the code will not compile. The compiler does not enforce this for unchecked exceptions. Common checked exceptions include IOException and SQLException. Common unchecked exceptions include NullPointerException and ArrayIndexOutOfBoundsException.
Since Error, Exception, RuntimeException, even Throwable are all concrete classes, Java allows any of of them to be instantiated directly; new RuntimeException() is a perfectly valid statement. However, doing so doesn't provide much information about the type of exception. Therefore, it's best to think of these four classes as abstract base classes, and only create and throw instances of more specific base classes. That way, for example, it's specified that the exception is a FileNotFoundException rather than a generic Exception. A good rule of thumb is to be as specific as possible both when throwing and catching exceptions. The Java API provides dozens of exception classes, and you can also define your own subclasses, so it's easy to chose an appropriate exception for a particular error condition.
Throwing exceptions
If your program encounters an unexpected condition it may throw an exception
Catching exceptions
Raising an exception in response to an error (or some other exceptional condition) is called throwing. To complete the metaphor, responding to an exception is called catching. Exceptions which are thrown can then be caught. In Java, every catch block must be preceded by a try block.
What makes exception so convenient is that you can write the code that should execute if no error occurs in a single try block, and the exception handling code in one or more catch blocks. This way, our main program logic is kept separate from the error handling logic, rather than intwined with the error handling code.
Many beginning programmers believe that the best thing to do with exceptions is to catch them as early and often as possible, and that the worse possible thing is an uncaught exception. Indeed, the compiler encourages this behavior by insisting the developer either catch checked exceptions, or declare them in the throws clause of the method.
In truth, deciding when and where to catch exceptions requires much more thought than simply sprinkling your code with try...catch blocks. And perhaps counter-intuitively, an uncaught exception isn't necessarily the worst thing when it comes to debugging.
If an exception is not caught, it will "bubble," or propogate, up the call stack until it is either caught or it reaches the top of the call stack. The call stack keeps track of the method-call chain in the reverse order in which methods are called. So the most recently called method is at the top of the stack. For example, imagine that methodA() calls methodB(), which calls methodC(), which in turn calls methodD(). If methodD() throws an exception, or calls another method which throws an exception, that exception will bubble up to methodC(). Unless the exception is caught along the way, it'll then bubble up to to methodB() and finally to methodA(). If methodA() still doesn't catch the exception, the exception stack trace will be dumped to the console, and the thread that called methodA() will terminate. Often, that means that the program itself will terminate.