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 before reading this tutorial. If you click on the link, 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).
Code Example
public class Exception {
public static void main(String[] args) {
exception();
} // main
public static void exception() {
String s = null;
if (s.length() > 1) {
System.out.println("string length > 1");
} // if
} // exception
} // Exception
Check your answer by running the code example above on Odin. Below, you will find a video demonstrating how to quickly compile and run this code in an unnamed package. 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 an annotated version of the output produced by the example code (yours may vary slightly):
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)
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 below:
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.
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.
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
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.
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 soon.
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)
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 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
was created due to the program dividing
by zero.
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.