Exceptions should be used only to denote exceptional conditions; they should not be used for ordinary control flow purposes. Catching a generic object such as Throwable
is likely to catch unexpected errors; see ERR08-J. Do not catch NullPointerException or any of its ancestors for examples. When a program catches a specific type of exception, it does not always know from where that exception was thrown. Using a catch
clause to handle an exception that occurs in a distant known location is a poor solution; it is preferable to handle the error as soon as it occurs—or to prevent it if possible.
The nonlocality of throw
statements and corresponding catch
statements can also impede optimizers from improving code that relies on exception handling. Relying on catching exceptions for control flow also complicates debugging because exceptions indicate a jump in control flow from the throw
statement to the catch
clause. Finally, exceptions need not be highly optimized, as it is assumed that they are thrown only in exceptional circumstances. Throwing and catching an exception frequently has worse performance than handling the error with some other mechanism.
Noncompliant Code Example
This noncompliant code example attempts to concatenate the processed elements of the strings
array:
public String processSingleString(String string) { // ... return string; } public String processStrings(String[] strings) { String result = ""; int i = 0; try { while (true) { result = result.concat(processSingleString(strings[i])); i++; } } catch (ArrayIndexOutOfBoundsException e) { // Ignore, we're done } return result; }
This code uses an ArrayIndexOutOfBoundsException
to detect the end of the array. Unfortunately, since ArrayIndexOutOfBoundsException
is a RuntimeException
, it could be thrown by processSingleString()
without being declared in a throws
clause. So it is possible for processStrings()
to terminate prematurely before processing all of the strings.
Compliant Solution
This compliant solution uses a standard for
loop to concatenate the strings.
public String processStrings(String[] strings) { String result = ""; for (int i = 0; i < strings.length; i++) { result = result.concat( processSingleString( strings[i])); } return result; }
This code need not catch ArrayIndexOutOfBoundsException
because it is a runtime exception, and such exceptions indicate programmer errors, which are best resolved by fixing the defect.
Applicability
Use of exceptions for any purpose other than detecting and handling exceptional conditions complicates program analysis and debugging, degrades performance, and can increase maintenance costs.
Bibliography
[Bloch 2001] | Item 39, "Use Exceptions Only for Exceptional Conditions" |
[JLS 2013] | Chapter 11, "Exceptions" |
3 Comments
David Svoboda
First, the applicability section has text completely duplicated from the Intro.
Second, the NCCE does not support the rule, it merely demonstrates a problem wrt catching NullPointerException. It shows that catching multiple exceptions don't play with each other well. Besides, since there are no null values in the array, the example is contrived.
The text after the 1st NCCE prob belongs in the intro...if it had some text to back it up.
I've also become less convinced over the years that using exceptions for 'nonexceptional' control flow is always a Bad Thing. The big problem you get is that exception-handling code is wedded to your exception ontology (eg you have ArrayIndexOutOfBoundsException rather than ArrayException, or some such). Clearly using the for loop is smaller code. But that means that using exceptions for nonexceptional behavior is a maintenance and performance issue, rather than a correctness or security issue.
David Svoboda
I've addressed these points.
Dhruv Mohindra
Catching ArrayIndexOOB exception in CS is unnecessary and hypocritical.
Not marking this as reviewed.