| Home > Core Java FAQ
> Exception Handling FAQ |
| Exception
Handling |
| |
|
Q .
What is an exception?
|
Ans :
An
exception is a condition (typically an error condition) that
transfers program execution from a thrower (at the source of the
condition) to a catcher (handler for the condition); information
about the condition is passed as an Exception or Error
object.
An
exception provides a communication channel between one portion of
code that detects and signals an (error) condition, and another
portion of code that responds to the condition. Exceptions in some
ways resemble method invocations, as highlighted in Table 2.8. In
both cases, information and control of execution pass from one
portion of code to another. And in both cases, the initiator of
the exchange typically doesn't know, and doesn't need to know,
precisely what code will respond.
Table
2.8: Throwing an Exception versus Invoking a Method
| Instigator/Requester |
Communication
mechanism |
Responder/Performer |
| throw
statement |
Exception
object |
executes
catch clause |
| method
invocation expression |
dynamic
method lookup; arguments and return value |
executes
method body |
It
is important to remember, however, that exceptions should be used
only for exceptional control flow needs. Exceptions
typically signal unexpected error conditions, such as:
IllegalArgumentException:
a method argument violates some requirement
NullPointerException:
a method is invoked on a null reference
ArrayIndexOutOfBoundsException:
an array index is too small or too large
The
Java language represents exceptions as objects, all of which
belong to the Throwable class or one of its
subclasses. Because exceptions belong to classes, you can readily
define your own exception types by subclassing an existing
exception class.
There are four main ways to interact
with Java's exception system, listed below in a common order that
programmers learning Java encounter them:
- catching
exceptions thrown by other people's code
- writing your own
code to throw exceptions
- declaring
exceptions that a method can throw
- defining your
own Exception classes
What follows is a
bare-bones how-to introduction for each of these facets.
To catch an exception, you specify a
body of code to watch, a type of exception to watch for, and a
body of code to execute if an appropriate exception type is thrown
within the watched body of code. For example:
int anIntValue;
String str = solicitStringFromUser();
try {
anIntValue = Integer.parseInt(str);
} catch (NumberFormatException e) {
reportError("string could not be parsed as an Integer.")
}
To
throw an exception, use the throw statement and
provide a newly created Exception instance, which can
include a message providing information about this particular
exception:
int speed; // an instance variable
public void setSpeed(int value) throws IllegalSpeedException {
if (value < 0) {
throw new IllegalSpeedException(
"speed cannot be negative");
}
speed = value; // executed only for valid speed values.
}
The above code also
illustrates how to declare an exception: you provide a throws
declaration following the method's parameter list. Note that both
methods and constructors can throw and declare exceptions.
Finally, to define a new Exception
class, subclass Exception or one of its subclasses,
and provide appropriate constructors. The IllegalSpeedException
used in the previous example is not a predefined Java class; you
could define it yourself:
class IllegalSpeedException extends Exception {
public IllegalSpeedException() { super(); }
public IllegalSpeedException(String s) { super(s); }
}
|
|
Q . Why does the compiler complain about InterruptedException when I try to use Thread's sleep method?
|
Ans
:
The
compiler is complaining, as it is required to do, that your code
invokes a method that might throw a checked exception,
but your code is not prepared to handle that exception; to stop
the compiler from complaining, your method must either declare or
catch the InterruptedException that Thread's
sleep method can throw.
InterruptedException
belongs to the set of checked exceptions— a subset of
exceptions that a Java compiler is required to track through any
source code it handles. One of the compiler's restrictions is that
a method declare all the checked exceptions that it might throw. A
method can throw an exception either by an explicit throw
statement or by invoking another method that throws the exception.
Thread's sleep method
declares that it can throw an InterruptedException;
therefore, the compiler will examine any method that invokes sleep
to check whether the exception is being dealt with. You have two
options when writing a method that invokes sleep:
- declare that
your method can throw
InterruptedException
- catch the
exception inside your run code
To
declare the exception, add a throws clause to your method
definition:
public void myMethod() throws InterruptedException { /* ... */ }
This is a simple
fix, but it also means that you are leaving it up to some other
method to catch and deal with the exception.
It is often better to catch an
exception right at the point it is generated, where you usually
have more information about what triggered the exception. To catch
the exception, surround your call to sleep in a try-catch
block:
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
// ... handle the exception here (good)
// or leave this blank to ignore the exception (risky)
// but still satisfy the compiler
}
|
|
Q .
Why do methods have to declare the exceptions they can throw?
|
Ans
:
The simple
answer is that the Java language requires it; the more meaningful
answer is that the language requires exception declarations
because they enhance the usability and robustness of code as part
of an API.
The
Java language requires that a method declare any checked
exceptions that the method might throw. Whether an exception
is checked or not depends on the exception's class. As shown in
Table 2.9, the base Throwable class splits into two
main subclass branches, Error and Exception.
All Exception subclasses are checked exceptions
except for RuntimeException and its subclasses.
Table
2.9: Checked Expections versus Unchecked Exceptions
| Class
hierarchy under Throwable |
Checked? |
Throwable |
Error
and its subclasses |
no |
Exception |
RuntimeException
and its subclasses |
no |
all
other Exception subclasses |
yes |
The
intent of checked exceptions is that you declare exceptions as a
meaningful part of your class's programming interface. Together
with a method's return value, exceptions define the output
behavior of the method. Any code that invokes a method must be
prepared to handle either of these:
- the method's
return value, if the method completes normally
- any of the
method's checked exceptions, if the method terminates
abnormally
Checked exceptions
thus complement the return value as indicators of a method's exit
status.
For example, the InputStream
class in the java.io package includes a read
method that is declared as follows:
public byte read() throws IOException;
This means that any
method invoking this read method must be prepared to handle two
kinds of outcome from the method: a byte return value if the read
succeeds, or an IOException if the read fails. The
invoking method itself must therefore either catch the exception
or declare that it, too, throws IOException.
The requirement that checked
exceptions be declared or caught is not just good programming
practice—it is a rule enforced by the Java compiler. More
specifically, the compiler enforces the following condition:
| If
method X invokes method Y, and method Y can throw a
checked exception, then either method X must catch the
exception, or method X must declare the exception (or a
superclass of the exception). |
The real power of this check is that
the compiler performs it transitively, following the often-complex
chain of possible method invocations (method X invokes method Y,
which in turn might invoke method Z, and so on).
This moderate degree of automated
error checking provides surprisingly strong help in writing
robust, error-tolerant code. One example comes from the
development of Java and HotJava themselves. When exception
declarations and exception checking were added to the language,
this immediately turned up a number of cases in which the HotJava
developers hadn't noticed they needed to catch certain important
types of exceptions. Instead of being caught further down the road
as tricky run-time bugs, these mistakes were now being flagged as
compile-time errors. The developers thus were able to find and fix
the problems much more efficiently. The extra effort of declaring
checked exceptions was repaid many times. Such experiences advise
against circumventing these built-in checks without strong reason
(and even then, think twice).
|
|
Q . What's the difference between a runtime exception and a plain exception-why don't runtime exceptions have to be declared?
|
Ans
:
The Java
language specifies that all runtime exceptions are exempted from
the standard method declarations and compiler checks; such
exceptions belong more to the system as a whole than to the method
that happens to be executing when the exception is thrown.
The
Java language lets you signal conditions (usually error
conditions) by throwing an object at one point in your
code, such that an enclosing block can catch the object
and infer from it the trigger condition. The object you throw must
belong to the Throwable class or one of its
subclasses. The class hierarchy under Throwable
further classifies the nature of the unusual condition. Throwable
subdivides into two subclasses, which The Java Application
Programming Interface (Vol. 1) demarcates nicely:
Error:
"indicates serious problems that a reasonable application
should not try to catch." (p. 175)
Exception:
"indicates conditions that a reasonable application might
want to catch." (p. 162)
Errors
are never declared—they are entirely unexpected and practically
always fatal. When an Error instance is thrown and
not caught, it works its way up the method invocation stack until
the Java Virtual Machine detects it, prints out a diagnostic error
message (usually including a useful stack trace), and then kills
that thread. (Uncaught Exception instances have this
same behavior.)
Exceptions come in two basic
varieties-those that can be ascribed to the execution of a single
method and those that belong more to the system as a whole:
Exception
(in general): meaningful, specific, recoverable condition that
can be expected to arise occasionally in the execution of this
method (for instance, a NumberFormatException
thrown when trying to parse a String instance as
an Integer value).
RuntimeException:
a condition that can arise in principle anytime, largely
independent of which particular method happens to be executing
(for instance, an ArrayIndexOutOfBoundsException).
Exceptions
in general must be declared by methods (and by constructors) and
are checked by the compiler. Runtime exceptions are exempted from
this because the Java designers judged that having to declare them
would be too much work, would involve too many methods, and would
not pay back the extra effort sufficiently in terms of increased
program robustness.
The difference between runtime
exceptions and other exceptions is not always clear-cut— it
requires human judgment as to the relative merits of including the
exception as a method-specific condition versus a can
happen-anytime general condition. Nevertheless, making the
distinction reflects Java's pragmatic streak:
- It presents a
balance between theoretical purity and practical needs.
- It helps
developers write convenient yet robust code.
Recommendation:
Use general (declared) exceptions wherever possible. In the
experience of many Java developers, the benefits of automatic
compile-time checking more than offset the extra effort of
declaring or catching exceptions.
|
|
Q . Given a method that doesn't declare any exceptions, can I override that method in a subclass to throw an exception?
|
Ans
:
No;
subclasses must honor the API contract established by their
superclasses, and this includes the types of checked exceptions
that a method can throw.
An
API (application programming interface) establishes a contract of
intent, not just of form or interpretation. To borrow terminology
from linguistics and philosophy, an API contract involves both
extension and intension: the boundaries of the current state of
the world (extension) as well as the intended boundaries for other
possible states of the world (intension: possible future
implementations). In object-oriented programming, a common source
of "possible future implementations" is subclassing from
an existing class in an API.
A method defines a contract for any
subclass method that would override it; it constrains possible
implementations that a subclass could provide. In Java, the bare
minimum contract for a method's inputs and outputs is the
following:
- A method's
parameter list is fixed; an overriding method in a subclass
must declare precisely the same number and types of arguments.
- A method's
return type is fixed; an overriding method in a subclass must
declare precisely the same return type.
- The set of
checked exceptions a method can throw (the method's declared
exception classes and all their subclasses) establishes an
upper bound. An overriding method in a subclass cannot throw
any checked exceptions outside of that; it can, however, throw
fewer exception types, or even none at all.
Note that the
contract on exceptions concerns only checked exceptions; errors
and runtime exceptions (that is, Error, RuntimeException,
and their subclasses) are always permitted.
If a method, such as Object's
toString method, is declared as throwing no checked
exceptions, any overriding method you define must live within
those bounds. You cannot define your own subclass of Exception
and have your toString method throw that. In such a
case, if you really need some exception to be thrown, you can
resort to a subclass of RuntimeException, which is
not checked or constrained by the compiler.
|
|
Q . Why do I get the java.lang.UnsatisfiedLinkError when I run my Java program containing Native Method invocations?
|
Ans : Your program is not able to find your shared library or DLL.
On Windows 95/NT, make sure that the DLL exists in a path that is included within the PATH environment variable. (This need is true for
both standard (untrusted) applications and trusted applets. At least,
if you use the Java Plug-in to give yourself standard Java inside a browser).
On Solaris, make sure that the environment variable LD_LIBRARY_PATH includes the path of your shared library.
Note that jdb looks for libraries with "_g" appended to their names. Thus, if you intend to use jdb on a Java application that invokes
native methods, you must ensure that the appropriately named libraries
are in jdb's path. The "debug" nm libraries can simply be renamed copies of the nondebug libraries.
For example, if your app invokes native methods in a library named mynm.dll (on Windows) or mynm.so (on Solaris), make a copy in the same
directory and name it mynm_g.dll or mynm_g.so.
|
|
Q .
Variable may not have been initialized.
|
Ans
:
URL test;
try {
test = new URL("http://osprey.avs.dec.com");
} catch (MalformedURLException e) {
System.out.println("bad URL:" + e.getMessage());
}
System.out.println("this is url " + test);
The compiler will warn you if you use a variable before it is certain to have been initialized (not just with the default value)
since this means you probably forgot to set it.
In the case of exceptions, you have to consider that the flow of control may terminate abruptly, with no operations completed. In the
example above, if an exception is raised in the try clause, variable
test will not be assigned a value, yet you are using it after the catch
clause. One solution would be to declare test with an explicit initial
value of null, but this works only because toString() works on a null
reference. (toString() is invoked implicitly by operator + with String
operand.)
Always initialize to a value that will work notwithstanding exceptions being thrown.
|
|
Q . No constructor {superclass}()...?
|
Ans
:
I extended the class called Frotz, and the compiler is giving me an
error message "No constructor Frotz()" in the child class. Why?
When you define a constructor for a class, the compiler inserts a call to the
superclass' parameterless constructor unless you explicitly call the
superclass' constructor at the start of your constructor. If
the superclass doesn't *have* a parameterless constructor, the compiler emits a message to that effect. The solution is usually to call the
superclass' constructor at the start of your constructor.
|
|
Q .
No constructor matching
MyCheckbox(myApplet)
|
Ans
:
MyApplet.java:11: No constructor matching
MyCheckbox(myApplet)
found in class MyCheckbox.
bp1 = new MyCheckbox(this);
^
If a compiler isn't finding a constructor you thought you created,
check whether you gave a return value to the method (remember, constructors have no return value). E.g.,
public void MyCheckbox( Container parent )
If you did, the compiler will think it is an ordinary method, not a constructor. This is a common mistake and hard to spot.
|
|
|
|