Lets consider the situation where you have the provided code of:
public void delete() throws IOException, SQLException { // Non-Compliant
/* ... */
}
The danger here is that the code that you write to call delete()
will look like:
try {
foo.delete()
} catch (Exception e) {
/* ... */
}
This is bad too. And it will be caught with another rule that flags catching the base Exception class.
The key is to not write code that makes you want to write bad code elsewhere.
The rule that you are encountering is a rather common one. Checkstyle has it in its design rules:
ThrowsCount
Restricts throws statements to a specified count (1 by default).
Rationale: Exceptions form part of a method's interface. Declaring a method to throw too many differently rooted exceptions makes exception handling onerous and leads to poor programming practices such as writing code like catch(Exception ex). This check forces developers to put exceptions into a hierarchy such that in the simplest case, only one type of exception need be checked for by a caller but any subclasses can be caught specifically if necessary.
This precisely describes the problem and what the issue is and why you shouldn't do it. It is a well accepted standard that many static analysis tools will identify and flag.
And while you may do it according to language design, and there may be times when it is the right thing to do, it is something that you should see and immediately go "um, why am I doing this?" It may be acceptable for internal code where everyone is disciplined enough to never catch (Exception e) {}
, but more often than not I've seen people cut corners especially in internal situations.
Don't make people using your class want to write bad code.
I should point out that the importance of this is lessened with Java SE 7 and later because a single catch statement can catch multiple exceptions (Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking from Oracle).
With Java 6 and before, you would have code that looked like:
public void delete() throws IOException, SQLException {
/* ... */
}
and
try {
foo.delete()
} catch (IOException ex) {
logger.log(ex);
throw ex;
} catch (SQLException ex) {
logger.log(ex);
throw ex;
}
or
try {
foo.delete()
} catch (Exception ex) {
logger.log(ex);
throw ex;
}
Neither of these options with Java 6 are ideal. The first approach violates DRY. Multiple blocks doing the same thing, again and again - once for each exception. You want to log the exception and rethrow it? Ok. The same lines of code for each exception.
The second option is worse for several reasons. First, it means that you are catching all the exceptions. Null pointer gets caught there (and it shouldn't). Furthermore, you are rethrowing an Exception
which means that the method signature would be deleteSomething() throws Exception
which just makes a mess further up the stack as people using your code are now forced to catch(Exception e)
.
With Java 7, this isn't as important because you can instead do:
catch (IOException|SQLException ex) {
logger.log(ex);
throw ex;
}
Furthermore, the type checking if one does catch the types of the exceptions being thrown:
public void rethrowException(String exceptionName)
throws IOException, SQLException {
try {
foo.delete();
} catch (Exception e) {
throw e;
}
}
The type checker will recognize that e
may only be of types IOException
or SQLException
. I'm still not overly enthusiastic about the use of this style, but it isn't causing as bad code as it was under Java 6 (where it would force you to have the method signature be the superclass that the exceptions extend).
Despite all these changes, many static analysis tools (Sonar, PMD, Checkstyle) are still enforcing Java 6 style guides. It's not a bad thing. I tend to agree with a warning these to still be enforced, but you might change the priority on them to major or minor according to how your team prioritizes them.
If exceptions should be checked or unchecked... that is a matter of great debate that one can easily find countless blog posts taking up each side of the argument. However, if you are working with checked exceptions, you probably should avoid throwing multiple types, at least under Java 6.
When you encounter exceptions within the logger itself, you shouldn't use the logger to log its own exceptions. The reason for that is that:
You may be stuck in an infinite loop. Imagine that within your logger, you have a conditional branch which wasn't tested (and generates an exception). Imagine that once the condition is met, any further reported exception is handled by the same branch. This means that from the moment the branch is executed, you're in an infinite loop.
You may be stuck in a temporary loop, generating thousands of exceptions per second. Imagine you're reporting exceptions to a remote server. An issue with the server causes another exception, which causes another one, and so on, until the connection is back.
What you should do instead is to fallback to a safer way to log the exceptions. For example, if your logger sends the exceptions to a remote server, send the exceptions within the logger to syslog
instead. If your logger records exceptions in Windows Events and this action fails, store the failure exception in a simple text file.
Once you have that, the next question is how do you know that those exceptions occurred: if you have dozens of applications running on thousands of servers, you can't possibly SSH each of them on regular basis to check whether they were logging something locally.
One way is to have a cron job which checks for those “exceptional logs” and pushes them to the location where other exceptions are stored (eventually using your logger, but beware of infinite or temporary loops!).
Best Answer
You might think that the
public static void main
method in Java or themain
function in C is the real entry point of your program – but it isn't. All high-level languages (including C) have a language runtime that initializes the program, and then transfers control flow to the entry point. In the case of Java, initialization will include:main
is invoked. These blocks aren't supposed to throw exceptions.There are a variety of ways to implement exception handling, but for the purpose of this question, they all can be viewed as a black box. The important thing however is that the language runtime must always provide an outermost exception handler that catches all exceptions that aren't caught by user code. This exception handler will usually print out a stack trace, shut down the program in an orderly fashion, and exit with an error code. Properly shutting down the program includes destroying the object graph, invoking finalizers, and freeing resources such as memory, file handles, or network connections.
For purposes of illustration, you can imaging the runtime wrapping all code in a giant try-catch that looks like
except that it's not necessary for a language to actually execute code like this. The same semantics can be implemented in the code for
throw
(or equivalent) that searches for the first applicable exception handler.