Declaring ExceptionsJ8 Home « Declaring Exceptions
In this lesson we learn how to use the throw
keyword which allows to throw exceptions from within our methods. We also investigate how to declare exceptions using the throws
keyword.
Before we look at using the throw
and throws
keywords lets refresh our memory with the different types of exceptional conditions that can occur in Java:
- The
Error
class and its subclasses handle error situations from within theJVM
itself and so are outside our control as programmers. As a general rule we can't recover from anError
situation and because of this we are not expected to handle anError
when it occurs. - The
Exception
class and its subclasses are what concern us more as programmers and these can be split into two categories:- Runtime exceptions: Are exceptions that are not checked for by the compiler and for this reason are also known as unchecked exceptions. Generally exceptions of type
RuntimeException
originate from problems in our code, such as an attempt to divide by zero which gives anArithmeticException
. These sorts of errors are within our control and as such should be handled by the code itself. - Checked exceptions: Are exceptions that are checked for by the compiler and if present and not declared, will give a compiler error. An example of this is a
FileNotFoundException
where a file we want to read may or may not be available. Whether the file is available or not is outside our control. So when we read the file, we need to handle or declare that this exception, or a superclass of it may occur.
- Runtime exceptions: Are exceptions that are not checked for by the compiler and for this reason are also known as unchecked exceptions. Generally exceptions of type
Using the throw
Keyword Top
We can use the throw
keyword to throw exceptional conditions from within our code. At the top of the exception hierarchy is the Throwable
class, which all other error and
exception classes inherit from. So we can throw any subtype of the Throwable
class if we want to, which includes objects of type Error
, Exception
and RuntimeException
.
The following code uses the throw
keyword to rethrow an error to a RuntimeException
:
package info.java8;
/*
A DivideByZero class
*/
public class DivideByZero {
public static void main(String[] args) {
/*
We put the code we think might cause a problem in a try block
*/
try {
int a = 5 / 0; // JVM
will not like this
}
/*
We handle the exception in a catch block and rethrow it
*/
catch (ArithmeticException ex) {
System.out.println("We caught exception: " + ex);
throw new RuntimeException(); // No need to handle or declare Error or RuntimeException
}
}
}
Save, compile and run the DivideByZero
class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running our reworked DivideByZero
class. We catch the ArithmeticException
exception and rethrow it as a RuntimeException
.
We could also do the same for Error
objects by just rethrowing with something like throw new Error();
. Lets have a look at throwing a checked exception, remember these are exceptions
that don't inherit from RuntimeException
and are checked for by the compiler. So a checked exception needs to be handled or declared or we get a compiler error. Time to run the
DivideByZero
class again and this time we will throw a checked exception:
package info.java8;
/*
A DivideByZero class
*/
public class DivideByZero {
public static void main(String[] args) {
/*
We put the code we think might cause a problem in a try block
*/
try {
int a = 5 / 0; // JVM
will not like this
}
/*
We handle the exception in a catch block and rethrow it
*/
catch (ArithmeticException ex) {
System.out.println("We caught exception: " + ex);
throw new Exception(); // Need to handle or declare checked Exception
}
}
}
Save and compile and the reworked DivideByZero
class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running our reworked DivideByZero
class. We catch the ArithmeticException
exception and rethrow it as an Exception
. If you
remember from our the Exception Hierarchy Diagram the Exception
class is a checked exception so we need to handle or declare the exception, or we get a
compiler error. This is what has happened here as we have neither handled or declared the rethrown exception. We will look at how to declare checked exceptions next.
Using the throws
Keyword Top
The compiler demands that we either handle or declare exceptions which are neither error or runtime exceptions, in other words checked exceptions. We looked at handling exceptions
in the last lesson, now we will look at declaring exceptions using the throws
keyword. To show this we will recode the DivideByZero
class to declare that it throws an
exception of type Exception
.
package info.java8;
/*
A DivideByZero class
*/
public class DivideByZero {
public static void main(String[] args) throws Exception { // Declare checked exception
try {
int a = 5 / 0; // JVM
will not like this
}
/*
We handle the exception in a catch block and rethrow it
*/
catch (ArithmeticException ex) {
System.out.println("We caught exception: " + ex);
throw new Exception(); // Need to handle or declare checked Exception
}
}
}
Save, compile and run the recoded DivideByZero
class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of compiling and running our reworked DivideByZero
class. This time the code compiles fine as we are declaring the Exception
checked exception
that we rethrow. We catch the ArithmeticException
exception and rethrow it as an Exception
and the exception propogates up the stack and our program ends.
Propogating Exceptions Up The Stack Top
In the previous example we used the throws
keyword to propogate the Exception
checked exception up the stack. In this example we didn't catch the exception higher up
the stack and the program eventually fell over with the Exception
checked exception in the main()
method. In this part of the lesson we are going to learn how we can deal with
chaining exceptions up the stack by declaring and ducking them. We will use a slideshow to demonstrate some calls and methods on the stack and what happens with an exception when it propogates
up the stack.
Now we have seen the slideshow, we will write the PropogateStack
class.
package info.java8;
/*
A class where we propogate an exception up the stack
*/
public class PropogateStack {
public static void main(String[] args) {
System.out.println("We are in Main().");
PropogateStack ps = new PropogateStack();
try {
ps.MethodA(ps);
}
catch (Exception ex) {
System.out.println("We finally catch the propogated exception in main() " + ex);
}
finally {
System.out.println("Closing application.");
}
}
public void MethodA(PropogateStack ps) throws Exception { // Declare checked exception
System.out.println("We are in MethodA().");
ps.MethodB(ps);
}
public void MethodB(PropogateStack ps) throws Exception { // Declare checked exception
System.out.println("We are in MethodB().");
ps.MethodC(ps);
}
public void MethodC(PropogateStack ps) throws Exception { // Declare checked exception
System.out.println("We are in MethodC().");
try {
int a = 5 / 0; // JVM
will not like this
}
catch (ArithmeticException ex) {
System.out.println("We caught exception: " + ex);
throw new Exception(); // Rethrow a checked Exception
}
}
}
Save, compile and run the PropogateStack
class in directory c:\_FlowControl in the usual way.
The above screenshot shows the output of running the PropogateStack
class and the control flow was explained in the slideshow, so we won't repeat it here.
Overridden Methods & Exceptions Top
There are a few things to remember when overriding methods that throw exceptions:
- An overriding method can throw any
Error
orRuntimeException
exceptions, whether these are declared in the overridden method or not.
Following is an example of the above:
package info.java8;
/*
A Test class
*/
public class A {
public void methodA() {
System.out.println("methodA() in class A.");
}
}
/*
B Test class
*/
public class B extends A {
public void methodA() throws RuntimeException {
System.out.println("methodA() in class B.");
throw new RuntimeException();
}
}
/*
Test A and B classes
*/
public class TestAB {
public static void main(String[] args) {
A a = new A();
a.methodA();
A b = new B();
b.methodA();
}
}
The above screenshot shows the output of compiling classes A
, B
and TestAB
and then running the TestAB
class. As you can see we can declare any unchecked exceptions
in the overriding method, whether these are declared in the overridden method or not.
- An overriding method must not throw any new checked exceptions or any checked exceptions that are higher up the inheritance tree than those declared in the overridden method.
Following is an example of the above:
package info.java8;
/*
A Test class
*/
public class A {
public void methodA() {
System.out.println("methodA() in class A.");
}
}
/*
B Test class
*/
public class B extends A {
public void methodA() throws IllegalAccessException {
System.out.println("methodA() in class B.");
}
}
The above screenshot shows the output of compiling classes A
and B
. Class B
gives a compilation error as we are trying to declare an unchecked exception that has not
been declared in the superclass A
.
- An overriding method can throw checked exceptions that are lower in the inheritance tree than those declared in the overridden method, or throw fewer or none of the checked exceptions that were declared in the overridden method.
Following is an example of the above:
package info.java8;
/*
A Test class
*/
public class A {
public void methodA() throws IllegalAccessException {
System.out.println("methodA() in class A.");
}
}
/*
B Test class
*/
public class B extends A {
public void methodA() {
System.out.println("methodA() in class B.");
}
}
/*
Test class
*/
public class TestAB {
public static void main(String[] args) {
try {
A a = new A();
a.methodA();
A b = new B();
b.methodA();
}
catch (Exception ex) {
System.out.println("Exception caught:" + ex);
}
}
}
The above screenshot shows the output of compiling classes A
, B
and TestAB
and then running the TestAB
class. As you can see we can declare any checked exceptions
in the overridden method and the overriding method can choose whether to declare these or not.
Lesson 3 Complete
In this lesson we learnt how to use the throw
keyword which allows to throw exceptions from within our methods. We also investigate how to declare exceptions using the throws
keyword.
What's Next?
In the next lesson we learn how to create our own exceptions and use them in our code.