- COMP.CS.140
- 5. Inheritance
- 5.4 Exception handling in Java, part 2
Exception handling in Java, part 2¶
This section will briefly introduce Java’s exception class hierarchy and discuss how to implement a custom exception class and cause (throw, or raise) an exception.
Earlier examples have involved e.g. the exception types IOException
and
NumberFormatException
. To be more precise, these are classes that the Java class library uses
for exception handling. All Java’s error handling classes have a superclass named Throwable
,
and in fact the try
-catch
statement can only catch exceptions of type Throwable
. That
class has two direct subclasses, Exception
and Error
, that also act as general
superclasses. Exception
is designated as the superclass of all classes that are used in typical
exception handling. Error
is related to very abnormal problems where usual exception handling
would not be useful.
All usual exception classes of the Java class library are subclasses of Exception
. The class
has e.g. the following public constructors and member functions:
Constructors
Exception()
andException(String msg)
. The latter stores the messagemsg
into the exception object, and the former works in same manner asException(null)
, that is, it storesnull
as the message.The message can be used to convey some additional information about what caused the exception.
Member function
getMessage()
that returns the message stored in the exception object.Member function
printStackTrace()
that prints to the standard output information about the active function call chain and the source file code line where the exception originated from.
It would be possible to use just the superclass Exception
for exception handling. Its
subclasses, such as IOException
, usually do not add any new functionality to it. The motive for
using different exception classes is not about functionality of the exception classes themselves.
The main benefits are (1) the ability to define separate exception handlers for different types of
problems and (2) the fact that the exception class names, mentioned e.g. in exception
specifications or catch
blocks, on their own convey some information about what type of a
problem they correspond to.
It is quite simple to define a custom exception class: extend the class Exception
and, if
required, implement a constructor that passes its parameters to the superclass.
One can raise an exception explicitly in Java by using a throw
statement of form
throw exceptionObject
. The mechanism is very similar with C++. The main difference is that Java
allows to throw only exceptions of type Throwable
whereas C++ permist pretty much all types.
The thrown exception object is often created with the new
operator in the throw
statement,
e.g. as throw new IOException("Errod reading line 3")
.
Below is an example of defining and using a custom exception class DivideByZeroException
. The
classes should be placed into separate files.
// A very simple exception class DivideByZeroException.
public class DivideByZeroException extends Exception {
public DivideByZeroException(String msg) {
super(msg);
}
}
public class DivideByZeroTest {
public static double divide(int numerator, int denominator)
throws DivideByZeroException { // Declare what type of exceptions may be thrown.
if(denominator == 0) { // Are we trying to divide by zero?
// The message can describe more details about the problem.
throw new DivideByZeroException(String.format("Trying to compute %d/%d!",
numerator, denominator));
}
return (double) numerator / denominator;
}
public static void main(String[] args) {
int num = 7;
for(int i = -2; i <= 2; i++) {
try {
System.out.format("%d/%d = %.3f%n", num, i, divide(num, i));
}
catch(DivideByZeroException e) {
System.out.println(e); // Prints (at least usually) the exception type and message.
}
}
}
}
The example program outputs:
7/-2 = -3.500
7/-1 = -7.000
DivideByZeroException: Trying to compute 7/0!
7/1 = 7.000
7/2 = 3.500
Because Exception
is a superclass of all (typical) exception classes, any type of exception can
be caught by a catch
block using the header catch(Exception e)
. Such so-called “catch all”
blocks should be used cautiously due to how they hide information about the more detailed exception
type. Catch all blocks do have their uses, even quite often, whenever we wish to catch all feasible
types of exceptions. Recall that Java forces us to acknowledge only “checked” exceptions, and hence
e.g. the Java compiler or IDE’s usually do not direct the programmer to handle unchecked exceptions.
Since it is not very easy to deduce all possible unchecked exception types from the code, it might
be convenient to use a catch all block; it should ensure that we do not omit any possibly occurring
exception type. It is also good to note that being categorized as “unchecked” does not mean that an
exception is harmless or rare. E.g. NullPointerException
, which is caused by trying to refer to
a class member via a null
reference, is a very common unchecked exception.
When an exception occurs, the program execution jumps to the nearest catch
block that is
compatible with the occurred exception type (or if no such block is found, the program will be
terminated due to an unhandled exception). Here it is good to note that if a try
block is
followed by several compatible catch
blocks, the first of them will be selected. Therefore if
you use a catch all block, it always needs to be that last catch
block. Otherwise the following
catch
blocks would be useless as the preceding catch all block would catch all exceptions
before them. Below is an example of a try
-catch
statement whose last catch
block would
never be reached.
void doSomething(String s) {
try {
Integer.parseInt(s);
}
catch(Exception e) {
System.out.println("An exception was caught: " + e);
}
catch(NumberFormatException e) {
System.out.println("I would never get to print this exception: " + e);
}
}
The Java compiler actually enforces the preceding rule. If you try to compile this example, the Java compiler will give more or less the following kind of an error message:
SomeJavaFile.java:xy: error: exception NumberFormatException has already been caught
catch(NumberFormatException e) {
^