Throwing Exceptions - Reading
Introduction
An exception is an event that, while a program is executing, disrupts the flow of a program. Recall that programs execute line by line, plodding along. An exception could occur and cause the program to suddenly stop and say “Hey! Wait a minute! Something’s not right”.
Let’s look at an example. Suppose that we have a method that tries to calculate the sum of all numbers in an array:
public class ArraySum {
public int calculateSum(int[] values) {
int sum = 0;
for (int i = 0; i <= values.length; i++) {
sum = sum + values[i];
}
System.out.println("The sum is: " + sum);
return sum;
}
}
You can create a new class in BlueJ called ArraySum
, copy/paste the code above into it, compile the class, instantiate it, and run it. Once you call the method you can pass in an array of values as below:
When you click OK you call the method calculateSum
with the array {1, 2, 3}
. Since 1+2+3=6, the returned value should be 6 and we should see “The sum is 6” printed out to the Terminal. When we run this method this is what happens:
Yikes! The method did not return anything back. Instead an exception was thrown. Specifically, on line 5, a java.lang.ArrayIndexOutOfBoundsException
was thrown. This is Java’s way of telling you “Hey you asked me to get the element at index 3 in the array. I tried but this index is bigger than the array! You asked me to do something impossible, I just gotta give up!!”. Moreover we do not see the “The sum is 6” printed out to the Terminal.
Also notice that in the Terminal that "ArraySum.calculateSum(ArraySum.java:5)"
is underlined. That means you can click on it and BlueJ will jump to the location in your code that threw the exception. Line 5 is where we are getting the i’th value of the array. This implies that the value of i at one point becomes 3, but there are only 3 elements in the array with index [0, 1, 2].
Let’s add a print statement to the code to print the current index to the Terminal and re-run the code:
We know that i
should only go through [0, 1, 2], yet it also goes to 3. That implies that the way in which we’re generating i
is incorrect. In fact, looking more closely at line 4, the boundary condition in the for loop is incorrect and should be less than, not less than and equals to. Fixing it fixes the method:
Difference between an error and an exception
Let’s cover another example that you’re familiar with from the Duke online courses. Suppose we want to use the indexOf()
method for a string to figure out if another string is contained inside it. Then we’ll use substring()
to get the exact substring back again. Here’s some code to explain:
public class MyExceptionTester {
public void run() {
String toLookInside = "bork";
int firstIndex = toLookInside.indexOf("k");
System.out.println("Index of k: " + firstIndex);
System.out.println("Get substring for k's location: " +
toLookInside.substring(firstIndex, firstIndex+1));
int secondIndex = toLookInside.indexOf("z");
System.out.println("Index of z: " + secondIndex);
System.out.println("Get substring for z's location: " +
toLookInside.substring(secondIndex, secondIndex+1));
}
}
Here’s what happens:
Let’s break it down!
-
The print statement on line 6 prints out “Index of k: 3”. That means that “k” is at index 3 in the string “bork”. That makes sense, because the 0th character is b, 1st character is o, 2nd character is r, then 3rd character is k.
-
The print statement on line 7 gets the substring that starts at index 3 inclusive, and ends at index 4 exclusive. That retrieves the 3rd character, which is “k”. Great!
-
The print statement on line 11 prints “-1” because the character “z” is not contained inside “bork”. This is exactly what
indexOf()
is meant to do. You’ve seenindexOf()
before during the Duke Coursera courses. Refer back to he Duke Learning to Program reference for more information. -
There are no more printed lines! Instead we got an exception on line 13. That means on line 13 something so egregious happened that Java said “No way! I can’t even keep running!!”.
- The error message tells us what happened, a
StringIndexOutOfBoundsException
. We tried to access the character at index -1 in the string. But recall that string characters indices start at 0. -1 doesn’t even make sense!
- The error message tells us what happened, a
Why does indexOf()
return -1 when it can’t find a substring? Why wouldn’t it throw some sort of exception, like CouldNotFindSubstringException
? Exceptions are what you throw when some part of your program can no longer fulfill its purpose. The purpose of indexOf()
is to tell you the location of a substring if it exists, else -1 if it does not exist. It is fulfilling its purpose just fine. Hence it returns -1 if it can’t find a substring.
Why does substring()
throw an exception if we attempt to access a character at index -1? substring()
can’t fulfill its purpose because -1 just doesn’t make sense. The purpose of substring()
is to return some subset of a string. By giving it an index of -1 it thinks something terrible has gone wrong and there isn’t any point in continuing to run the program any more.
How to throw your own exceptions
In general, if you write code that makes assumptions or requires something to be true, yet your code then discovers these assumptions don’t hold, you throw an exception.
Here is an example. Suppose we would like to write code to create a Body Mass Index (BMI) calculator. BMI is defined as (weight in kilograms) / (height in meters squared). Here’s some code that correctly implements this:
public class BmiCalculator {
public double calculate(double weightKilograms, double heightMeters) {
return weightKilograms / (heightMeters * heightMeters);
}
}
For example, if weight is 100kg and height is 2m then the BMI is 100 / (2 * 2) = 100 / 4 = 25.0.
Can you see the two problems here?
-
Height could be 0, and we’d end up dividing by 0. This will cause an
ArithmeticException
to be thrown, which could confuse people using our function. We could throw a more useful exception to help callers understand what went wrong. -
Weight or height could be negative. This makes absolutely no sense, and if someone gives us a negative weight or height by accident we need to throw an exception because BMI no longer works in that case.
Here is how we would use java.lang.IllegalArgumentException
to guarantee that our assumptions hold before running the calculation code. IllegalArgumentException
comes with Java and is used to mean “I’m a method and you gave me an invalid argument. I can’t keep running!”.
public class BmiCalculator {
public double calculate(double weightKilograms, double heightMeters) {
if (heightMeters <= 0) {
throw new IllegalArgumentException("height is unexpectedly zero or " +
"negative with value: " + heightMeters);
}
if (weightKilograms <= 0) {
throw new IllegalArgumentException("weight is unexpectedly zero or " +
"negative with value: " + weightKilograms);
}
return weightKilograms / (heightMeters * heightMeters);
}
}
Notice that there is a constructor for IllegalArgumentException
that accepts a String as an argument. This String shows up in the Terminal for users when the exception is thrown. Here is how the exception looks if I pass in 0 for heightMeters
and 1 for weightKilograms
:
Since the constructor argument for IllegalArgumentException
is a String
, you could use String.format
to create a String
that contains the values of other strings. Here are some examples of using String.format
: https://beginnersbook.com/2017/10/java-string-format-method/
null
and NullPointerExceptions
Another example of invalid inputs to methods would be a null
object for a method argument like a String
. You already know about null
and how null
references can cause NullPointerExceptions
! Please refer back to Week 3 of the second Duke course.
What happens when an exception is thrown?
In this reading exceptions are fatal, as in the whole program comes crumbling to a halt. In future classes you will learn how to handle exceptions and keep your program running!