Runtime errors can be divided into low-level errors that involve violating constraints, such as:
Logical errors can lead to low-level errors if they are not detected. Often, it is better to detect them (to provide better feedback).
Errors can arise due to:
Note that recovery is often not possible at the point of the error (because the error may occur inside some utility function that doesn't know anything about the overall program or what error recovery should involve). Therefore, it is desirable to "pass the error up" to a level that can deal with it.
There are several possible ways to handle errors:
ret = g(x); if (ret == ERROR_CODE) { ... } else f(ret);instead of just:
f(g(x));
Idea:
Exceptions can be built-in (actually, defined in one of Java's standard libraries) or user-defined. Here are some examples of built-in exceptions with links to their documentation:
Catch exceptions using try blocks:
try { // statements that might cause exceptions // possibly including function calls } catch ( exception-1 id-1 ) { // statements to handle this exception } catch ( exception-2 id-2 ) { // statements to handle this exception . . . } finally { // statements to execute every time this try block executes }
Notes:
public static void main(String[] args) { InputStream istream; File inputFile; try { inputFile = new File(args[0]); istream = new InputStream(inputFile); // may throw FileNotFoundException } catch (FileNotFoundException ex) { System.out.println("file " + args[0] + " not found"); } }
Notes:
java.io.FilenotFoundException: foo at java.io.FileInputStream ... at ... at Test.main ...would be printed. (Actually, if there were no try/catch for the FileNotFoundException, the program wouldn't compile because it fails to list that exception as one that might be thrown. We'll come back to that issue later...)
If the finally block does include a transfer of control, then that takes precedence over any transfer of control executed in the try or in an executed catch clause. So for all of the cases listed above, the finally clause would execute, then its transfer of control would take place. Here's one example:
try { return 0; } finally { return 2; }
Note that this is rather confusing! The moral is that you probably do not want to include transfer-of-control statements in both the try statements and the finally clause, or in both a catch clause and the finally clause.
Only uncaught checked exceptions need to be listed in a function's
throws clause. Unchecked exceptions can be caught in a try block, but if
not, they need not be listed in the function's throws clause.
Consider the following program (assume that comments are replaced
with actual code that works as specified):
Question 1:
Assume that function f might throw exceptions Ex1, Ex2, or Ex3.
Complete function g, outlined below, so that:
Question 2:
Consider the following function.
A. f(0, X, "hi");
Part B.
Checked and Unchecked Exceptions
Every exception is either a checked exception or an unchecked
exception.
If a method includes code that could cause a checked exception to be
thrown, then:
So in general, you must always include some code that acknowledges the
possibility of a checked exception being thrown.
If you don't, you will get an error when you try to compile your code.
+--------+
| Object |
+--------+
|
|
+-----------+
| Throwable |
+-----------+
/ \
/ \
+-------+ +-----------+
| Error | | Exception |
+-------+ +-----------+
/ | \ / | \
\________/ \______/ \
+------------------+
unchecked checked | RuntimeException |
+------------------+
/ | | \
\_________________/
unchecked
Choices when calling a function that may throw an exception
Note that if your code might cause a checked exception to be
thrown; i.e.,:
then your function must include a throws clause listing all such
exceptions. For example:
public static void main(String[] args) throws FileNotFoundException, EOFException
{ // an uncaught FileNotFoundException or EOFException may be thrown here }
class TestExceptions {
static void e() {
// might cause any of the following unchecked exceptions to be thrown:
// Ex1, Ex2, Ex3, Ex4
}
static void d() {
try {
e();
} catch (Ex1 ex) {
System.out.println("d caught Ex1");
}
}
static void c() {
try {
d();
} catch (Ex2 ex) {
System.out.println("c caught Ex2");
// now cause exception Ex1 to be thrown
}
}
static void b() {
try {
c();
} catch (Ex1 ex) {
System.out.println("b caught Ex1");
} catch (Ex3 ex) {
System.out.println("b caught Ex3");
}
}
static void a() {
try {
b();
} catch (Ex1 ex) {
System.out.println("a caught Ex1");
} catch (Ex4 ex) {
System.out.println("a caught Ex4");
// now cause exception Ex1 to be thrown
}
}
public static void main(String[] args) {
a();
}
}
Assume that this program is run four times.
The first time, function e throws exception Ex1,
the second time, it throws exception Ex2, etc.
Foe each of the four runs, say what is printed;
if an uncaught exception is thrown, say what happens.
How to Define and Throw Exceptions
public class EmptyStackException extends Exception { }
Note: New exceptions must be subclasses of Throwable; as
discussed above, they are usually subclasses of Exception (so that
they are checked). The exceptions you define do not have to be
public classes; however, remember that if you do not make them
public, then they can only used in the package in which they are
defined.
public class Stack {
Note:
...
}
public Object Pop() throws EmptyStackException {
if (Empty()) throw new EmptyStackException();
}
...
static void g() throws ... {
try {
f();
} catch ( ... ) {
...
} ...
}
static void f(int k, int[] A, String S) {
int j = 1 / k;
int len = A.length + 1;
char c;
try {
c = S.charAt(0);
if (k == 10) j = A[3];
} catch (ArrayIndexOutOfBoundsException ex) {
System.out.println("array error");
throw new InternalError();
} catch (ArithmeticException ex) {
System.out.println("arithmetic error");
} catch (NullPointerException ex) {
System.out.println("null ptr");
} finally {
System.out.println("in finally clause");
}
System.out.println("after try block");
}
Part A.
Assume that variable X is an array of int that has been initialized
to be of length 3.
For each of the following calls to function f, say what (if anything) is
printed by f, and what, if any, uncaught exceptions are thrown by f.
B. f(10, X, "");
C. f(10, X, "bye");
D. f(10, X, null);
Why doesn't f need to have a throws clause that lists the uncaught
exceptions that it might throw?
try {
// statements (including function calls) that might cause an exception
} catch ( exception-1 id1 ) {
// code to handle the first kind of exception
} catch ( exception-2 id2 ) {
// code to handle the second kind of exception
} ...
} finally {
// code that will execute whenever this try block does
}
Solutions to Self-Study Questions