Exceptions
Exceptions Defined
Exceptions are used to report back on exceptional conditions that were encountered at runtime and which prevents the continuation of the method or scope you are in.
Exceptions are thrown from the location where the exception conditional was detected and catched (handled) by something capable of reacting to the exceptional condition.
Explicit use of throw statements provides an alternative to the old-fashioned style of handling error conditions by returning funny values, such as the integer value -1 where a negative value would not normally be expected. Experience shows that too often such funny values are ignored or not checked for by callers, leading to programs that are not robust, exhibit undesirable behavior, or both.
When To Use Exceptions
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 or problem.
With an exceptional condition, you can't 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 the problem to a higher context where something is capable and qualified to make the proper decision.
For example a normal problem that can be handled without throwing an exception:
public int divide(int value, int denominator) {
int result = 0;
if (denominator != 0) {
result = value / denominator;
}
return result;
}
In this example the method itself knows how to deal with the exceptional condition of trying to divide a value by 0.
For example the same problem but in this case the method itself does not know how to deal with the exceptional condition. The exception is reported back to the caller of the method.
public int divide(int value, int denominator)
throws DivideByZeroException {
if (denominator == 0) {
throw new DivideByZeroException();
}
return value / denominator;
}
 | Note
Don't use exceptions for every error condition.
Don't create a custom exception class for every possible error condition. Be pragmatic and try to use high level exception classes. |
Exception Types
java.lang.Throwable is the root class for all exceptions. The two direct subclasses are java.lang.Error and java.lang.Exception. Another important base class is java.lang.RuntimeException, which inherits from java.lang.Exception. These classes are the base classes in java's exception mechanism.
Together with these base classes java also defines two groups of exceptions: checked and unchecked exceptions. The table below describes the differences between the two groups.
Table 1.4. Exception Types
| Group |
Description |
| Checked |
- Must be declared in the throws clause of the method from which they are thrown
- Must be handled (caught). The fact if they are handled is determined at compile time
- Inherit from java.lang.Exception
|
| Unchecked |
- Must not be declared in the throws clause of the method
Remark: Although it is a good practice to add it to the throws clause since it can be useful to the developer using the method informing him/her of what could possibly go wrong (for example: java.lang.IllegalArgumentException)
- Must not be caught
Remark: it is a good practice to never catch unchecked exceptions.
- Inherit from java.lang.RuntimeException of java.lang.Error
|
Throwing Exceptions
Exceptions thrown by a method are declared in the throws clause of the method signature. A method can throw more than one exception.
Be specific and comprehensive with the throws clause. Don't be lazy and just include the super class in the throws clause if subclass exceptions are being thrown (see also Exception Matching).
Add a javadoc description for each exception being thrown and the reason why it could be thrown.
As stated earlier, java defines two groups of exceptions: checked and unchecked exceptions. Not every exception thrown by a method needs to be a checked exception (in other words: inherit from java.lang.Exception). The type of exception that needs to be thrown depends on the context in which it is generated.
 | Tip
Try to limit the number of exceptions thrown by a method. Think about the poor developer having to catch and handle them all (see also Catching Exceptions). |
Catching Exceptions
Exceptions thrown by some method eventually need to be handled (caught) somewhere. The location where the exception must be caught is dependent of the context in which it is used.
You can catch an exception to:
- React to the exceptional condition being reported by, for example, presenting an error dialog to the user
- Take the exception and re-throw it again or wrap it in another exception (see also exception chaining).
The first scenario is quite straightforward. The second one can for example be used to limit the number of exceptions being thrown by a method or to give another meaning to an exception that was encountered.
Suppose you have a method that needs to call four other methods and that each of these four methods throw a different exception. If you are unable to handle these exceptions in the context of your method you will need to report them back to whoever called your method.
public void myMethod()
throws Exception1, Exception2,
Exception3, Exception4 {
otherMehod1();
otherMethod2();
otherMethod3();
otherMethod4();
}
A better approach is to catch all of the individual exceptions and wrap them in one common exception.
public void myMethod() throws MyException {
try {
otherMehod1();
otherMethod2();
otherMethod3();
otherMethod4();
} catch (Exception1 e) {
throw new MyException(e);
} catch (Exception2 e) {
throw new MyException(e);
} catch (Exception3 e) {
throw new MyException(e);
} catch (Exception4 e) {
throw new MyException(e);
}
}
Cleaning Up After An Exception Using A finally Clause
When handling (catching) exceptions there is often some piece of code that you want to execute whether or not an exception is thrown within the try block. This can be achieved by adding a finally block to the end of the try/catch block.
The fact if you need to use the finally clause entirely depends on the context in which the exception if being handled. There are however scenarios where you always have to include a finally clause:
- Release (non-memory) resources (streams, socket connections, JDBC resources, ...) that were created in the try/catch block.
- Restore the object state back to its original state before returning
For example a scenario in which a stream that was opened is always closed even if exceptions were raised.
public void readFile(String filename)
throws IOException {
char buffer[] = new char[1024];
FileReader iStream = null;
try {
iStream = new FileReader(new File(filename));
iStream.read(buffer);
} catch (IOException e) {
throw e;
} finally {
if (iStream != null) {
iStream.close();
}
}
}
If the finally clause would not have been added in the example above you would run the risk that the input stream remains open when reading the file failed.
 | Note
The finally clause must not always be preceded by a catch clause. The example above can be also be written as:
public void readFile(String filename)
throws IOException {
char buffer[] = new char[1024];
FileReader iStream = null;
try {
iStream = new FileReader(new File(filename));
iStream.read(buffer);
} finally {
if (iStream != null) {
iStream.close();
}
}
}
|
Exception Matching
When an exception is thrown the exception handling mechanism in the JVM looks for the nearest handlers (catch clause) in the order they are written. When it finds a match, the exception is considered handled, and no further searching occurs. This matching process does not require a perfect match between the exception that was thrown and the handler. A derived-class object will match a handler for the base class.
Take for example the java.io.IOException. The IOException acts as a base class for several other IO related exceptions (FileNotFoundException, EOFException, ...). Catching (handling) the root IOException involves catching all derived exception instances. See example below:
public char[] readFile(String filename)
throws IOException {
char buffer[] = new char[1024];
FileReader iStream = null;
try {
iStream = new FileReader(new File(filename));
iStream.read(buffer);
} catch (IOException e) {
throw e;
} finally {
if (iStream != null) {
iStream.close();
}
}
return buffer;
}
In some cases however you might want to react differently to a certain type of exceptional condition. The example shows the same readFile method but this time the method reacts differently to the fact that file could not be found.
public char[] readFile(String filename)
throws IOException {
char buffer[] = new char[1024];
FileReader iStream = null;
try {
iStream = new FileReader(new File(filename));
iStream.read(buffer);
} catch (FileNotFoundException e) {
} catch (IOException e) {
throw e;
} finally {
if (iStream != null) {
iStream.close();
}
}
return buffer;
}
There is no real guideline when it comes to determining the level at which exception matching should occur. It all depends on the context in which the exception is being handled.
Exceptions And Error Messages
All exceptions are capable of providing additional information describing the exceptional condition in more detail. More specific each exception can hold:
- A message
- A stack trace describing the location where the exception originated
For exceptions to be useful it is important that the message with which it was constructed provides sufficient information describing the exceptional condition. If your application is meant to be internationalized the internationalization must also be reflected in the exception's error messages. This is especially the case for exceptions that hold messages that eventually will need to be presented to a user.
There is nothing that stops you from adding additional attributes to you custom exceptions if these attributes will help you in providing more detailed feedback to whoever will have to handle the exception.
Additional information that could be added to your custom exception classes could for example be an error code. This last attribute is particularly handy when you have to deal with internationalized exceptions. If a user calls you to say he (or she) got an error message in French and your French is a bit rusty you can always ask him (or her) for the error code and continue you investigation of the problem based on this code.
You can control the messages that are used in your custom exceptions by providing specific constructors that only accept certain arguments and that will create the detailed messages themselves.
For example:
public class FileNotFoundException extends Exception {
public FileNotFoundException(String filename) {
super("The file " + filename
+ " could not be found");
}
}
Another technique you could use is to make the exception constructor private and provide several, factory like, methods that would throw the exceptions.
For example:
public class BusinessException extends Exception {
private BusinessException(String message) {
super(message);
}
public static void throwLoadException(String something)
throws BusinessException {
throw new BusinessException("Error loading "
+ something);
}
public static void throwSaveException(String something)
throws BusinessException {
throw new BusinessException("Error saving "
+ something);
}
}
Alternatively you could create a LoadException and SaveException that inherit from a BusinessException and use the same approach as the FileNotFoundException from the first example.
Exception Chaining
Often you want to catch one exception and throw another, but still keep the information about the original exception wrapped inside the new exception being thrown. This technique is called exception chaining.
Since JDK1.4 all Throwable subclasses may take a cause object in their constructor. For code that needs to run on an older JDK version the developers will have to write their own versions of chainable exceptions.
 | Important
Be careful when using exception chaining in a multi-tiered environment. Sometimes the exception handler does not have all exception classes on its classpath. If a root exception has such an exception in its chain its possible for a ClassNotFoundException to be thrown when the root exception is serialized between the tiers. |
Creating Custom Exceptions
The Java development environment provides a lot of exception classes that you could use. You should go to the trouble of writing your own exception classes if you answer yes to any of the following questions.
- Do you need an exception type that isn't represented by those in the Java development environment?
- Would it help your users if they could differentiate your exceptions from those thrown by classes written by other vendors?
- Does your code throw more than one related exception?
- If you use someone else's exceptions, will your users have access to those exceptions?
- Should your package be independent and self-contained?
Exceptions And EJBs
Use Of RemoteException
EJB implementation methods, corresponding to a method defined in the EJB's remote, local or home (both remote and local), should never throw an java.rmi.RemoteException.
If the EJB method itself uses code that throws a RemoteException the exception should either be wrapped in a javax.ejb.EJBException or an application specific exception (a checked exception in the methods throws clause).
 | Note
This restriction is enforced by the EJB spec 1.1 and higher. Some containers check for this specification violation, others don't. |
For example consider someEjbMethod do be defined in the remote or local interface:
WRONG
public void someEjbMehod() throws RemoteException {
doSomething();
}
private void doSomething() throws RemoteException {
}
RIGHT
public void someEjbMehod() {
try {
doSomething();
} catch (RemoteException e) {
throw new EJBException(e);
}
}
private void doSomething() throws RemoteException {
}
Rolling Back Transactions
Checked exceptions thrown by an EJB implementation do not rollback any pending transactions. If an EJB throws a checked exception and it decides that all changes already made in the ongoing transaction should be rolled back it should do so explicitly by calling setRollbackOnly on the EJB's context.
For example:
public void MySessionEJB implements SessionBean {
private SessionContext context;
public void doSomethingCritical()
throws SomeCheckedExcetion {
try {
doSomething();
} catch (SomeCheckedException e) {
context.setRollbackOnly();
throw e;
}
}
}
Exception Handling Strategies
What goes up must come down. Every exception that is thrown needs to be caught somewhere by something. Once an exception is caught it is time do determine what to do with it. What follows is a brief description of possible exception handling strategies and which strategies to avoid (see also Catching exceptions).
A strategy determines what will happen when an exception is caught, in other words, the code in the catch clause of a try/catch block.
 | Important
Do not tackle exception handling at the end of the development cycle. Design your error handling strategy from the beginning. |
printStackTrace Is Not An Exception Handling Strategy
Although printStackTrace is not an exception handling strategy it is (unfortunately) the one that is encountered the most, especially in projects where exception handling was considered to be a low (or even no) priority.
The use of printStackTrace method should be avoided at all costs because:
- It only writes a stack trace to the standard error stream, usually the console. The only strategy that can be applied here is having someone sit down at the console and monitor it (or a file to which the console output is written).
- If the console output is not redirected to a file, the stack trace is lost.
- Once the stack trace is printed, the program merely continues on.
There are no scenarios (or excuses) in which the use of printStackTrace is justified. If the only option you have is to print the stack trace somewhere you should use a logging implementation (like the one provided in JDK 1.4 or Log4J).
Don't Use Empty catch Clauses
Never use an empty catch clause. If the exception is somewhat expected (a rare condition) you can add a comment stating why the exception is not important.
Example that handles a java.beans.PropertyVetoException:
public void doSomething() {
try {
} catch (PropertyVetoException e) {
}
}
If you think that an exception will never get thrown you are tempted to simply eat the exception; in other words: use an empty catch clause.
A better technique is to catch the unexpected exception and wrap it in a runtime exception. In this case you are sure that if the exception should occur anyway you will be aware of it.
For example:
public void doSomething() {
try {
callMethod();
} catch (UnexpectedException e) {
throw new Error("Unexpected exception", e);
}
}
 | Note
The example above uses the exception chaining feature available in JDK 1.4 to wrap the unexpected exception in an Error instance. |
Catch And Re-throw
This is the most common exception handling strategy in which an exception that is caught is re-thrown as is or translated into some other exception.
Eventually the exception reaches a method that does now what to do with it (or is unable to throw it further upwards). Once the exception has reached its final destination the following can be done with it:
- Log it with an appropriate severity level
- Present an error message to the user (only to be used in graphical clients, never on the server)