3.2. Exception Messages¶
Before we dive into the details of how to properly handle an exception in Java, we first need to better understand how exceptions work and how to interpret error messages produced by the JVM when an exception is not handled properly. Taking a few moments to understand the output from the JVM will allow you to quickly and confidently identify the problem and find a solution.
When an exception occurs, two things happen:
an exception object is created and thrown; and
the normal flow of control is disrupted.
The exception object that is created contains information about the location and cause of the exception.
You have likely encountered the dreaded NullPointerException in the past, so
we will use that as an example. If you click on the link in the previous
sentence, you will see the Java API documentation for the
NullPointerException
class. NullPointerException
is a regular
Java class that has constructors and methods that can be called from
your programs. If you haven’t seen a NullPointerException
before,
it’s easy to create a program that will generate one.
Test Yourself
Take a moment to carefully read through the program in the box below. In your notes, write what you expect the output to be along with an explanation of what should happen when the program is run. Don’t worry if you are unsure. Write something down and do the best you can.
It may help to look back at what it means for a reference variable to be set to set to null (Example 1 - Refer to No Object).
1public class Exception {
2 public static void main(String[] args) {
3 exception();
4 } // main
5
6 public static void exception() {
7 String s = null;
8
9 if (s.length() > 1) {
10 System.out.println("string length > 1");
11 } // if
12 } // exception
13} // Exception
Check your answer by running the code example above on Odin. If you need a quick refresher on the compilation commands, check out the video below. The video compiles the code to the default (unnamed package), but we recommend that you also practice compiling the code to a named package as well by adding a package statement (you choose the name) and the corresponding package directories before compiling. Take a few minutes to review the Named Package section of the Packages chapter if you need a refresher.
- When this code is run, the JVM:
Creates a
NullPointerException
object on the second line of theexception
method when it tries to call thelength
method on anull
reference; andDisrupts the normal flow of control to report to the user that the exception was thrown and abruptly terminates the program.
In the example above, the exception
method does not finish executing as
it normally would (because of the exception) and the program crashes.
Please note that the program doesn’t crash because an exception
occurred. Instead, it crashes because the exception was not handled
properly.
Here is the output produced by the example code (yours may vary slightly):
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local0>" is null
at Exception.exception(Exception.java:9)
at Exception.main(Exception.java:3)
When you see an error message like this, take a few seconds to read through the message to understand what it is saying. We will break it down line-by-line later in this section.
The indented lines starting with at
in the output are collectively
referred to as a stack trace. The stack trace tells the user which
methods were active when the program crashed in the order that they were
called (from the bottom up). This facilitates faster debugging by
allowing you to better understand what was happening in the application
when it crashed.
3.2.1. Understanding the Stack Trace¶
The stack trace, when read in reverse order (i.e., from bottom to top), tells us a story about what happened when we ran the program (output repeated below for convenience).
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local0>" is null
at Exception.exception(Exception.java:9)
at Exception.main(Exception.java:3)
In our example, the stack trace tells us that the main
method was
executed until the program got to line 3, then the exception
method
(Exception.exception
) was called and executed until the program got
to line 9, the origin of the exception.
Since the exception
method does not handle the exception, the
exception object propagated (i.e., thrown/passed back) to its
calling method. In general, exception objects will continue to propagate
back through the calling methods in the call stack (i.e., the methods we
see in the stack trace) until the program either: i) handles the
exception object; or ii) lets the exception propagate out of main
.
In our example, the exception propagated from exception
to main
,
and since the main
method does not handle the exception, the
exception continued to propagate out of main
and crash the program.
Any time an exception is allowed to propagate out of main
, the
program will crash. It’s our job to make sure that we catch exceptions
before they cause a crash. We will see how to do this later in this chapter.
Line-by-Line Breakdown of the Stack Trace Output
A. | Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.length()" because "<local0>" is null
B. | at Exception.exception(Exception.java:9)
C. | at Exception.main(Exception.java:3)
Line A: The top line in the output always displays the error
message contained in the exception object (usually the reason for
the exception) along with the exception type
(NullPointerException
in this case). The exception occurred
when the length
method was invoked on a null
reference.
With this information, we can look at our code to figure out that
s
must have been null
since s
is the only reference
variable on line 9.
Line B: This is the first line in the stack trace but the second line in the overall output. It indicates the origin of the exception; that is, it provides the class name, method name, filename, and line number where the exception object was first thrown during program execution. Here is a breakdown:
class name file name
| |
at Exception.exception(Exception.java:9)
| |
method name line number
Line C. The last line in the stack trace indicates the last executed line
of the first method executed by our program; in most cases, this is
implicitly the main
method since most Java programs start in
main
. Here is a breakdown:
class name file name
| |
at Exception.main(Exception.java:3)
| |
method name line number
Error messages produced when a program crashes from an exception, like the one shown above, are very informative for us as programmers; however, they are often confusing, startling, or even scary when encountered by end users who just witnessed the program crash and have no way to use the information in the error message. To prevent our users from seeing these error messages, we need to handle exceptions in one of two ways:
avoid them; or
handle them.
In your previous class, you likely attempted to avoid exceptions. We will focus on properly handling them throughout the rest of this chapter.
Test Yourself
A classmate knows they can’t post their source code to graded assignments on the class Piazza page, so they decide to post the output from an exception they received to see if anyone can help. The output looks like this:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at TicTacToeGame.calculateScore(TicTacToeGame.java:66)
at TicTacToeGame.isWinner(TicTacToeGame.java:61)
at TicTacToeTester.main(TicTacToeTester.java:23)
In your response on Piazza, answer the following questions to help them out:
Where did the exception originate? Mention the filename, method, and line number in your answer.
What type of exception was thrown and what is its FQN?
When the program started running, the
main
method was called. That method executed until it got to line 23. Continue the story for your classmate:What happened on line 23 of
TicTacToeTester.java
?What happened on line 61 of
TicTacToeGame.java
?What happened on line 66 of
TicTacToeGame.java
?
Test Yourself Solution (Don’t open until completing the question above)
The program started (as all programs do) in the main
method. From the output (bottom up), we can tell that the main
method is contained within the TicTacToeTester
class. The code
ran until it got to line 23 of the main
method where the
isWinner
method in the TicTacToeGame
class was called. The
code continued to line 61 of the isWinner
method where it then
called the calculateScore
method in the TicTacToeGame
class. The calculateScore
method ran until it got to line 66
where an ArithmeticException
(FQN:
java.lang.ArithmeticException
) object was created due to the
program dividing by zero. So, the exception originated on line
66
of TicTacToeGame
in the calculateScore
method.
Since the program crashed, we know the programmer did not properly
avoid or handle the exception. They should start by looking at
line 66 in TicTacToeGame.java
to try and find the bug.
It is also worth mentioning that, in Java, methods can either
return a value OR throw (propagate) an exception. In this case, the
exception originated in the calculateScore
method. Since it was
not handled, it was automatically propagated to the isWinner
method and then the main
method. The exception wasn’t handled
in any of these methods, so the program ultimately crashed.
Rapid Fire Review
What is an exception in Java?
A syntax error that occurs during compilation.
An event that occurs during the execution of a program that encounters an error or exceptional situation.
A type of loop in Java.
A command used to compile Java code.
When do exceptions occur in a Java program?
During compilation.
At runtime.
During linking.
When the program is being edited.
What happens when an exception occurs in a Java program?
The program continues running normally.
The program automatically fixes the error.
An exception object is thrown, and the normal flow of control is disrupted.
The program pauses and waits for user input.
What is a
NullPointerException
?An error caused by trying to access an element outside the bounds of an array.
An exception thrown when an application tries to use a null object reference.
An error that occurs during the compilation of a program.
A method that prints the length of a string.
What information does the top line of an exception message provide?
The total runtime of the program.
The type of exception and a brief description of what caused it.
The current value of all variables.
The location of the syntax error in the code.