Learning Outcomes
At the end of this lecture, you’ll be able to:
- Design robust methods with Exceptions.
- Write Java code utilizing Java Exceptions.
- Extend Java’s built-in exceptions to create custom exceptions.
- Differentiate when to throw exceptions and when to try/catch the exceptions.
- Write JUnit tests to check whether exceptions are thrown when expected.
- Explain the difference between checked vs. unchecked exceptions.
Lecture Plan
In this lecture, we'll cover the following lessons:
- Java Programs with Exceptions: A robust method
- Java Programs with Exceptions: Unit Testing
- Java Programs with Exceptions: Throw an Exception!
- Java Programs with Exceptions: Exercise⚡
- Java Programs with Exceptions: Custom Exceptions
- Java Interlude: Exception Hierarchies
- Java Interlude: Two Types of Exceptions
- Exercise: LengthException⚡
Lessons marked with ⚡ contain exercise/activity.
Downloads
Java Programs with Exceptions: A robust method ↗
Consider the specification of the IndexedList.get
method:
/**
* Retrieve the value stored at the given index.
*
* @param index representing a position in this list.
* Pre: 0 <= index < length
* @return value at the given index.
*/
T get(int index);
A client of get
(any method that calls it) is responsible to ensure the pre-condition is met. The get
method can check the validity of its input too. (It is not always the case that a method will be able to check and ensure its pre-conditions are met.)
A robust method is one that handles bad (invalid or absurd) inputs reasonably.
Java’s exception handling mechanism supports the construction of robust methods.
Let’s update the specification/declaration of IndexedList.get
to make it robust.
/**
* Retrieve the value stored at the given index.
*
* @param index representing a position in this list.
* @return value at the given index.
* @throws IndexOutOfBoundsException when index < 0 or index >= length.
*/
T get(int index) throws IndexOutOfBoundsException;
Notice we have removed the pre-condition. Instead, the method throws IndexOutOfBoundsException
when `index is out of range.
Resources
Here are some resources on robust coding:
Defensive programming is a closely related concept.
Java Programs with Exceptions: Unit Testing ↗
Let’s update IndexedListTest
to include tests to check the IndexOutOfBoundsException
is thrown when it is expected.
@Test
@DisplayName("get() throws exception if index is below the valid range.")
void testGetWithIndexBelowRangeThrowsException() {
try {
numbers.get(-1);
fail("IndexOutOfBoundsException was not thrown for index < 0");
} catch (IndexOutOfBoundsException ex) {
return;
}
}
@Test
@DisplayName("get() throws exception if index is above the valid range.")
void testGetWithIndexAboveRangeThrowsException() {
try {
numbers.get(size + 1);
fail("IndexOutOfBoundsException was not thrown for index > length");
} catch (IndexOutOfBoundsException ex) {
return;
}
}
Notice the approach to test that an exception is thrown when it is expected to be thrown:
try {
// call a method with an invalid input
obj.someMethodThatThrowsException(badInput);
// we expect the execution stops
// and goes to the catch block as a result of an exception
// if we get passed this point, exception was not thrown
// we must deliberately signal the test has failed!
fail("The expected exception was not thrown!");
} catch (ExpectedException ex) {
// if we are here, the expected exception is thrown;
// there is nothing to do, we can "return"
// to indicate the test has successfully passed
return;
}
The fail
method is part of JUnit’s Assertions
library
import static org.junit.jupiter.api.Assertions.fail;
Run the tests and see them fail!
Resources
If you need a refresher on Java’s Exception, please visit Exceptions on Oracle’s [online] Java Tutorial. Alternatively, you can read Baeldung article on Java Exceptions.
JUnit 5 includes assertThrows()
method that provides a more elegant approach to check an exception is thrown. We will not use this alternative approach because it involves the use of Java’s Lambda expression:
assertThrows(IndexOutOfBoundsException.class, () -> {
numbers.get(-1);
});
This post on howtodoinjava.com
provides a more detailed example on using assertThrows()
.
Java Programs with Exceptions: Throw an Exception! ↗
Let’s update ArrayIndexedList.get
to throw the IndexOutOfBoundsException
:
@Override
public T get(int index) throws IndexOutOfBoundsException {
if (index >= 0 && index < length()) {
return data[index];
} else {
throw new IndexOutOfBoundsException();
}
}
Make note of the syntax; in particular, be careful with throw
v.s. throws
keywords.
Run IndexedListTest
and see that all tests pass.
Java Programs with Exceptions: Exercise ↗
Exercise:
- Update the specification/declaration of
IndexedList.put
to make it robust. - Write unit tests for
put
to check exception is thrown when it is expected. - Update implementation of
ArrayIndexedList.put
to throw exception.
Solution
Update IndexedList.put
:
/**
* Change the value at the given index.
*
* @param index representing a position in this list.
* @param value to be written at the given index.
* Post: this.get(index) == value
* @throws IndexOutOfBoundsException when index < 0 or index >= length.
*/
void put(int index, T value) throws IndexOutOfBoundsException;
Add the following unit tests to IndexedListTest
:
@Test
@DisplayName("put() throws exception if index is below the valid range.")
void testPutWithIndexBelowRangeThrowsException() {
try {
numbers.put(-1, 10);
fail("IndexOutOfBoundsException was not thrown for index < 0");
} catch (IndexOutOfBoundsException ex) {
return;
}
}
@Test
@DisplayName("put() throws exception if index is above the valid range.")
void testPutWithIndexAboveRangeThrowsException() {
try {
numbers.put(size + 1, 10);
fail("IndexOutOfBoundsException was not thrown for index > length");
} catch (IndexOutOfBoundsException ex) {
return;
}
}
Update ArrayIndexedList.put
:
@Override
public void put(int index, T value) throws IndexOutOfBoundsException {
if (index < 0 || index > length()) {
throw new IndexOutOfBoundsException();
}
data[index] = value;
}
Aside: IndexOutOfBoundsException
is a built-in Java Exception which will be thrown e.g. when an array is indexed with a value out of index range. So, the statement data[index] = value;
will throw IndexOutOfBoundsException
when index
is invalid; we didn’t need to change the implementation of ArrayIndexedList.put
. I prefer the updated implementation as it provides more clarity to the behavior of the method.
Java Programs with Exceptions: Custom Exceptions ↗
Java comes with many built-in exceptions (see a list of common exceptions and their description here) such as the IndexOutOfBoundsException
. These exceptions cover most situations that are bound to happen in programs where there is a need to throw an exception. However, there will be times we need to supplement these built-in exceptions with our own.
Creating custom exceptions can be as simple as extending an existing built-in exception.
To learn the process, we make our own version of IndexOutOfBoundsException
. We call ours simply IndexException
.
Create a Java class
IndexException
with the following content:public class IndexException extends RuntimeException { }
Update
IndexedList
,IndexedListTest
andArrayIndexedList
: replace any usage ofIndexOutOfBoundsException
withIndexException
.
All built-in exceptions have a default constructor as well as an overloaded constructor that takes a String
(message
) as a parameter. It is considered good practice to provide a similar overloaded constructor for your custom exceptions.
/**
* Exception for invalid index. Data structures using (integer) indices
* throw IndexException if a given index is out of range.
*/
public class IndexException extends RuntimeException {
/**
* Constructs a new IndexException.
*/
public IndexException() {
}
/**
* Constructs a new IndexException with the specified detail message.
*
* @param message the detail message. The detail message is saved for
* later retrieval by the getMessage() method.
*/
public IndexException(String message) {
super(message);
}
}
Resource
Create a Custom Exception in Java by Baeldung is a good read.
Java Interlude: Exception Hierarchies ↗
The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement.
Exceptions are regular classes, and as such, one exception can subclass another.
class BankingException extends Throwable {...}
class InsufficientFundsException extends BankingException {...}
class NegativeAmountException extends BankingException {...}

If an exception is declared to be caught, any of the sub-classes of that exception will also be caught by that same catch statement.
try {
// Some code that might throw BankingException exception
// or its subclasses
} catch (BankingException e) {
// deal with the exception
}
When you chain catch
blocks, you must deal with more specific exceptions first.
try {
// Some code that might throw BankingException exception
// or its subclasses
} catch (InsufficientFundsException e) {
// deal with InsufficientFundsException
} catch (BankingException e) {
// deal with the exception
}
Java Interlude: Two Types of Exceptions ↗
There are two kinds of exceptions in Java: checked exceptions and unchecked exceptions. In this lesson, I’ll provide some examples to understand the distinction.
Checked Exceptions
Consider this code:
public void doSomething() {
doThisFirst();
// more code here!
}
If doThisFirst
method throws a checked exception, the compiler will not let you simply call it! You must handle the exception by either of these techniques:
- Using
try
/catch
block
public void doSomething() {
try {
doThisFirst();
} catch (SomeCheckedException e) {
// Deal with the exception.
}
// more code here!
}
- Allow the
doSomething
method to throw the exception raised bydoThisFirst
public void doSomething() throws SomeCheckedException {
doThisFirst();
// more code here!
}
A method that throws a checked exception must include a throws
clause in its declaration.
Java verifies checked exceptions at compile-time and forces you to provide a handling mechanism for it.
Unchecked Exceptions
All Java exceptions are checked unless they subtype the Error
class or the RuntimeException
class.

The compiler does not force you to handle unchecked exceptions. For instance, the compiler will let you simply call the doThisFirst()
if it (may potentially) throws an unchecked exception:
public void doSomething() {
doThisFirst();
// more code here!
}
A method that throws an unchecked exception does not need to include a throws
clause in its declaration.
Why two types of Exceptions?
The distinction between checked v.s. unchecked exception is specific to Java. In C++
for instance, all exceptions are unchecked, so it is up to the programmer to handle them or not.
Read Unchecked Exceptions — The Controversy on Oracle’s website for more information. In a nutshell, Java believes it must force programmers to handle exceptions. On the other hand, it makes allowance to declare errors that could happen, but rarely do, as unchecked exceptions. So, the programmer is not frustrated to e.g. have to write try/catch every time they use division in a program even though division by zero causes ArithmeticException
.
Resources
Checked and Unchecked Exceptions in Java by Baeldung is a good read.
Exercise: LengthException ↗
Exercise The constructor of ArrayIndexedList
should throw an unchecked LengthException
when the value provided for size
is $\leq 0$. Take all the necessary steps to make this happen (don’t forget writing tests).
Solution
Please refer to the posted solution.