Programs must not catch {{ Wiki Markup java.lang.NullPointerException
}}. A {{NullPointerException
}} exception thrown at runtime indicates the existence of an underlying {{null
}} pointer dereference that must be fixed in the application code (see rule [EXP01-J. Never dereference null pointers] for more information). Handling the underlying null pointer dereference by catching the {{NullPointerException
}} rather than fixing the underlying problem is inappropriate for several reasons. First, catching {{NullPointerException
}} adds significantly more performance overhead than simply adding the necessary null checks \[ [Bloch 2008|AA. References#Bloch 08]\]. Second, when multiple expressions in a {{try
}} block are capable of throwing a {{NullPointerException
}}, it is difficult or impossible to determine which expression is responsible for the exception because the {{NullPointerException
}} {{catch
}} block handles any {{NullPointerException
}} thrown from any location in the {{try
}} block. Third, programs rarely remain in an expected and usable state after a {{NullPointerException
}} has been thrown. Attempts to continue execution after first catching and logging (or worse, suppressing) the exception rarely succeed.
Likewise, programs must not catch RuntimeException
, Exception
, or Throwable
. Few, if any, methods are capable of handling all possible runtime exceptions. When a method catches RuntimeException
, it may receive exceptions unanticipated by the designer, including NullPointerException
and ArrayIndexOutOfBoundsException
. Many catch
clauses simply log or ignore the enclosed exceptional condition and attempt to resume normal execution; this practice often violates rule ERR00-J. Do not suppress or ignore checked exceptions. Runtime exceptions often indicate bugs in the program that should be fixed by the developer and often cause control flow vulnerabilities.
...
Noncompliant Code Example (Null Object Pattern)
...
)
This noncompliant code example is derived from the logging service null object design pattern described by Henney \[ [Henney 2003|AA. References#Henney 03]\]. The logging service is composed of two classes: one that prints the triggering activity's details to a disk file using the {{FileLog
}} class and another that prints to the console using the {{ConsoleLog
}} class. An interface, {{Log
}}, defines a {{write()
}} method that is implemented by the respective log classes. Method selection occurs polymorphically at runtime. The logging infrastructure is subsequently used by a {{Service
}} class.
Code Block | ||
---|---|---|
| ||
public interface Log { void write(String messageToLog); } public class FileLog implements Log { private final FileWriter out; FileLog(String logFileName) throws IOException { out = new FileWriter(logFileName, true); } public void write(String messageToLog) { // write message to file } } public class ConsoleLog implements Log { public void write(String messageToLog) { System.out.println(messageToLog); // write message to console } } class Service { private Log log; Service() { this.log = null; // no logger } Service(Log log) { this.log = log; // set the specified logger } public void handle() { try { log.write("Request received and handled"); } catch (NullPointerException npe) { // Ignore } } public static void main(String[] args) throws IOException { Service s = new Service(new FileLog("logfile.log")); s.handle(); s = new Service(new ConsoleLog()); s.handle(); } } |
...
An acceptable alternative implementation uses accessor methods to control all interaction with the reference to the current log. The accessor method to set a log ensures use of the null object in place of a null reference. The accessor method to get a log ensures that any retrieved instance is either an actual logger or a null object (but never a null reference). Instances of the null object are immutable and are inherently thread-safe.
Some system designs require returning a value from a method rather than implementing _do-nothing_ behavior. One acceptable approach is use of an exceptional value object that throws an exception before the method returns \[ [Cunningham 1995|AA. References#Cunningham 95]\]. This can be a useful alternative to returning {{ Wiki Markup null
}}.
In distributed environments, the null object must be passed by copy to ensure that remote systems avoid the overhead of a remote call argument evaluation on every access to the null object. Null object code for distributed environments must also implement the Serializable
interface.
...
Exception wrapping is a common technique to safely handle unknown exceptions. For another example, see rule ERR06-J. Do not throw undeclared checked exceptions..
*ERR08-EX1:* Task processing threads such as worker threads in a thread pool or the Swing event dispatch thread are permitted to catch {{ Wiki Markup RuntimeException
}} when they call untrusted code through an abstraction such as the {{Runnable
}} interface \[ [Goetz 2006|AA. References#Goetz 06], p. 161\].
ERR08-EX2: Systems that require substantial fault tolerance or graceful degradation are permitted to catch and log general exceptions such as Throwable
at appropriate levels of abstraction. For example:
...