Basic Guidelines on Java Exceptions for Programmers

Ensure that your call stack is prepared to handle any exceptions that may get thrown by your code.  Do not allow exceptions to unwind up the call stack without being handled and result in a crashed main program. 
Ensure that your code honors the "catch or specify" requirement for exception handling. Any block of code that could throw an exception must be enclosed either in a try-catch-finally block that handles the exception or a within a method that uses the "throws" clause to specify the exceptions it will pass up the call stack. 
Checked exceptions must adhere to the "catch or specify" requirement.  Exception handling for checked exceptions should be able to handle the exception and continue operation of the program.  Ensure that your programs manage state such that when a checked exception occurs the program can revert to a prior safe state and continue operation. [n.b. This is point is added for clarity really since all known compilers force this rule anyway.  - BL]
Error Exceptions (from the Error class or sub-classes - an "unchecked" exception) should also adhere to the "catch or specify" requirement even though this is not required by the Java API.  This ensures that the program gracefully shuts down from an error.  It also improves the internal documentation of the code and subsequent maintenance.  Exception handling for error exceptions should also include logging to help identify the source of the problem.
Runtime exceptions (from the RunTimeException class or sub-classes - an "unchecked" exception) should also adhere to the "catch or specify" requirement even though this is not required by the Java API.  This ensures that the program gracefully shuts down from a program logic error or "bug". It also improves the internal documentation of the code and subsequent maintenance.  Exception handling for runtime errors should also include logging to help identify the logic error so that a correction can be implemented.
Do not wrap checked exceptions or otherwise by-pass handling of exceptions.  This only helps the code compile but doesn't help (and actually hurts) the run-time reliability of the program.
For unchecked exceptions, programs may use "coarse-grained" exception handling with the exception handling done at a higher level than individual, low-level, classes or methods.  This is allowed because run-time exceptions can occur almost anywhere and adding extensive error-handling code for unchecked exceptions would unduly clutter the functional purpose of the code.
Always provide and use the finally block - even if it appears unnecessary.  It does not harm if unused and provides a mechanism for handling unanticipated exception types - unanticipated exceptions can be the most troublesome to detect and correct.  The finally block should also be used to "clean up" after any statements in the try block and to return the system to a predictable state.  Use the finally block to prevent resource leaks by ensuring that the resource is always released.
Do not implicitly reduce the fidelity of exception information by using handlers of high-level classes such as Exception or RunTimeException when exceptions of more detailed types can occur.  If you chose to generalize exception handling, do so explicitly.
When one exception leads to another, always chain the exceptions together.  Allowing related exceptions to exist as distinct, unchained, objects abandons useful information about the behavior of the system that created the exceptions.
Ensure that the call stack eventually evaluates exceptions to see if they are chained and provides exception handling logic based upon the results.  At a minimum, the logging system should capture the un-chained details from the exceptions.
Write your own exception classes only if you can demonstrate the following conditions are true:
    a.) You can justify a need for an exception type that isn't provided by the Java API (or a vendor API),
    b.) you can demonstrate that other programmers using your code need to be able to discriminate between the exceptions you throw and those provided by the Java API (or from classes provided by a vendor),
    c.) you have a need to package you code without dependencies on other packages for exception types,
    d.) or your code throws several exception types but has only one semantic meaning when it does this (or the reverse).
When writing your own exceptions, follow the conventions of the Java language for checked and unchecked exceptions.  Use checked for recoverable exceptions and unchecked for errors or runtime problems that are not recoverable and should lead to a shutdown of the program.
For any classes you write that are used as exception classes, append "Exception" to the name of the class.
Never use exceptions to handle the normal logic and flow of control for the program - use exceptions only for exceptional conditions.  Use the try-catch-finally of exception handling instead of nested if statements to handle the exceptions which should not, you hope, occur - e.g. system is out of memory.  Use conventional logic statements (if, case, etc.) for the various conditions in the business process which you know will occur - e.g. a payment is less than the amount owed.
Use the try-catch-finally and throws options for exception handling at a consistent level of abstraction.  For example, an aggregate class of several object types should not have an exception type in some cases handled by the contained object and in other cases by the aggregate. Use the Decorator and Delegate combination or the Wrapper to reconcile this disparity.
Don't ignore exceptions - e.g. don't write do-nothing catch blocks.  At a minimum, that an exception occurred should be logged.  For checked exceptions (such as file not found or memory not available), this assists the system sizing and capacity planning effort.  For errors and runtime exceptions, this provides data about the defects in the program.
Throw exceptions appropriate to the abstraction of your class and keep your exception classes appropriate to the associated class types in your program - e.g.  associate FileNotFoundExceptions with file handling classes, IOExceptions with I/O classes, ArrayOutOfBoundsException with array classes, etc.
Declare all fields of exception classes as "final" or "private".  The data carried in objects of exception classes should not be modified and should be protected from unauthorized or accidental modifications.
Do not write code that throws exceptions of type Throwable, Error or Exception.  These are too general to be meaningful to the exception handler.  Use one of the more specific exception classes.  Using these highest level types indicates your code won't bother to tell the user anything about what the problem is.
Do not write code that throws the exception NullPointerException.  This is an exception that programmers expect to be thrown by the Java Virtual Machine (JVM) and not from custom code.  Pick another defined exception type or write your own.
- Brian

No comments: