The contract defined by the throws clause is strictly enforcedyou can throw only a type of checked
exception that has been declared in the tHRows clause. Throwing any other type of checked exception is
invalid, whether you use throw directly or use it indirectly by invoking another method. If a method has no
throws clause, that does not mean that any exceptions can be thrown: It means that no checked exceptions
can be thrown.
All the standard runtime exceptions (such as ClassCastException and ArithmeticException) are
extensions of the RuntimeException class. The more serious errors are signaled by exceptions that are
extensions of Error, and these exceptions can occur at any time in any code. RuntimeException and
Error are the only exceptions you do not need to list in your throws clauses. They are ubiquitous, and
every method can potentially throw them. This is why they are unchecked by the compiler. Those runtime
exceptionssuch as IllegalArgumentExceptiontHRown under specific conditions should always be
documented, even though they do not appear in the tHRows clause; see Chapter 19 for details on how to
document exceptions thrown by your methods.
Because checked exceptions must be declared in a throws clause, it follows that any code fragment outside
a method, or constructor, with a throws clause cannot throw a checked exception. This means that static
initializers and static initialization blocks cannot throw checked exceptions, either directly or by invoking a
method that throws such an exception. Non-static initializers and non-static initialization blocks are
considered to be part of the constructor for a class, and so they are allowed to throw checked exceptions only
if all the constructors of the class declare those checked exceptions.
Checked exception handling is strictly enforced because doing so helps avoid bugs that come from not dealing
with errors. Experience has shown that programmers forget to handle errors or defer writing code to handle
them until some future time that never arrives. The tHRows clause states clearly which exceptions are being
thrown by methods and makes sure they are dealt with in some way by the invoker.
If you invoke a method that lists a checked exception in its tHRows clause, you have three choices:
- Catch the exception and handle it.
Catch the exception and map it into one of your exceptions by throwing an exception of a type
declared in your own throws clause.
•
Declare the exception in your throws clause and let the exception pass through your method
(although you might have a finally clause that cleans up first; see "finally" on page 288 for
details).
•
The first two choices require that you catch exceptions thrown by other methodssomething you will learn
about soon.
You should be explicit in your throws clause, listing all the exceptions you know that you throw, even when
you could encompass several exceptions under some superclass they all share. This is good
self-documentation. Deciding how explicit you should be requires some thought. If you are designing a
general interface or superclass you have to think about how restrictive you want to be to the implementing
classes. It may be quite reasonable to define a general exception you use in the interface's throws clause,
and to expect that the implementing classes will be more specific where possible. This tactic is used by the
java.io package, which defines a general IOException type for its methods to throw. This lets
implementing classes throw exceptions specific to whatever kind of I/O is being done. For example, the
classes that do I/O across network channels can throw various network-related subclasses of IOException,
and those dealing with files throw file-related subclasses.