Exceptions
Goals
- Throw an exception.
- Catch an exception.
- Re-throw an exception.
Concepts
- call stack
- catch
- checked exception
- exception
- handle
- raise
- throw
- unchecked exception
- unwind
Language
throw
try … catch(…) … finally
Javadoc
@throws
Library
java.io.IOException
java.io.UnsupportedEncodingException
java.lang.AssertionError
java.lang.Error
java.lang.Exception
java.lang.IllegalArgumentException
java.lang.IllegalStateException
java.lang.IndexOutOfBoundsException
java.lang.NullPointerException
java.lang.RuntimeException
java.lang.Throwable
java.lang.UnsupportedOperationException
Lesson
Things do not always go as planned. If a caller passes bad data to a method (e.g. the caller requests a method to divide by zero), or a method has problems accessing resources such as a file, it needs a way to report the problem back to the caller. Java provides a special facility for elegant error-handling, in line with most modern programming languages.
Return Codes
Historically there have been several approaches to error-reporting. One of the earliest was the use of special return values. If a method returns a value, there may be certain values that have special meaning.
We saw for example that the Point.calculateSlope(…)
method could return Double.POSITIVE_INFINITY
to indicate that the resulting slope would be vertical. But this is not an error condition; this is only a special condition. We expect vertical lines to exist just as horizontal lines exist; they merely need to be treated specially.
On the other hand there exist unexpected conditions that in a particular context should be understood as an error condition. Let's say that we want to create a method for a class called ComputerScreen
that will display a happy face ☺ on the screen at some coordinates. The method might look like this:
But what if one of the coordinates is negative? The happy face will never appear (which will make the user have a sad face ☹). This is not what the method expected to happen; how can it notify the calling method that something is not right? Long ago computer programs would use special return codes that represented error conditions. For example, the above method might be adapted to return a boolean
value indicating true
if success or false
for failure.
There are many problems with “special value” error-handling, including:
- It introduces artificial values; why do we need to return a value for showing a face?
- It requires special checking for each call of the method, which makes the code verbose.
- It intermingles error handling into the normal logic of the program, as shown in the following example.
The last point above is especially important: the program should tend to its normal processing of data without being distracted with error conditions. There needs to be a way to relegate error handling to a separate part of the program, outside the normal program flow.
Exceptions
Most modern programs now have the idea of exceptions. When an error condition is encountered, the program can create and then throw an exception object. (Some programming language say raise an exception.) Error handling can then be relegated to a central location in another part of your program to catch an exception that might have been thrown.
Throwing Exceptions
Throwing an exception bypasses the normal return
mechanism of a method. Processing of the method will immediately stop, and the call stack will unwind, as programmers say. That is, program execution will jump out of the current function to the previous, and then jump out of that method to the previous, and so on. This will continue until a special error-handling section of the code is reached.
One of the most common exceptions to throw is java.lang.IllegalArgumentException
, which indicates that an erroneous or inappropriate argument was passed as a parameter.
Exception Handling
Once you throw an exception, something has to catch the exception at some point. Catching and processing an exception is called handling the exception, and Java uses a special try … catch(…)
statement pair for doing this.
Here is how try … catch(…)
works:
- Somewhere up the call stack (that is, in one of the callers), put all your statements that can throw some related exception(s) inside a
try{…}
block. - Immediately beneath the
try{…}
block, put acatch(…){…}
block, indicating inside the parentheses the exceptions you want to catch. - (optional) Provide a
finally{…}
block for any code you want to always execute, whether or not an exception was thrown.
Checked Exceptions
Exception classes such as java.lang.IllegalArgumentException
, which derive from java.lang.RuntimeException
, are called unchecked exceptions.The special low-level error classes that derive from java.lang.Error
, such as java.lang.AssertionError
, are also unchecked exceptions. Any method can throw instances of unchecked exceptions, even if the method or its documentation never indicates that it throws exceptions. Similarly there is no requirement that you catch unchecked exceptions in your program, or otherwise deal with them.
All exceptions that do not derive from java.lang.RuntimeException
are called checked exceptions. These come with two requirements:
- You must indicate using the
throws
keyword that your method may throw that type of checked exception. - The calling method must:
- catch that type of exception, and/or
- declare that it throws that type of exception.
One common checked exception is java.io.IOException
, which indicates some sort of input/output error. Here is how the sendEmail()
method above might be implemented, as sending email requires communication over TCP/IP.
The calling method may catch the IOException
as well:
A calling method may instead decide not to catch a checked exception type, and instead simply declare that it too can throw that exception type, allowing the exception to “bubble up” the call stack if it is ever thrown.
Rethrowing Exceptions
Those two approaches—catching an exception or declaring the exception using throws
—are not mutually exclusive. Part of the code could be guarded by a try … catch(…)
statement, while other portions of the method could be unguarded and allow the exception to continue up the call stack. Even if an exception is caught, it can usually be rethrown simply by using the throw
keyword again.
“Check” Methods
If you find yourself using the same error-checking code over and over, you may consider creating a separate method to do the check and throw the exception if needed. The name of such a method usually starts with “check…
”, and acts as a simple wrapper around the error checking logic. If everything is OK, the check method does nothing. If there is a problem, the check method will throw the appropriate exception.
Review
Exceptions are conceptually revolutionary! They represent a huge departure from the old error-handling approach of checking the return value of each method. If you don't understand how exceptions represent a different paradigm than method return values, you should review this lesson.
- Throwing an exception skips the normal control flow and unwinds the stack, providing a completely different control path for returning from a stack of method calls.
- Exceptions allows you to consolidate error handling in a specific area of the program, at the call level(s) you choose.
Common Standard Exceptions
java.lang.NullPointerException
- A
null
argument was passed when a parameter doesn't allow it. Java throws this exception automatically when you try to access the member of anull
reference. java.lang.IllegalArgumentException
- An argument passed is inappropriate for the parameter. Technically an inappropriate
null
argument is also an “illegal argument” as well, but for null references Java convention is to use aNullPointerException
. java.lang.IndexOutOfBoundsException
- The index of an array or list it outside the range. This could technically be considered an “illegal argument” as well, but is conventionally more appropriate for invalid indexes.
java.lang.IllegalStateException
- The state of some values is unexpected or inappropriate to complete the operation requested in a method. In cases where both an argument is inappropriate because of an invalid object state, only throw
IllegalArgumentException
if there is exists some argument that could have worked with the current state; otherwise, if no argument would have worked, throw anIllegalStateException
. java.lang.UnsupportedOperationException
- An object does not support a method, such as a read-only object that does not support modification methods. This exception is useful to use temporarily if you add a necessary method but have not implemented it.
Gotchas
- Only throw an exception if the condition is truly unexpected and erroneous. “Infrequent but valid” is not the same thing as “unexpected”.
- If you need to guarantee that something happens after an operation, use a
try … finally(…)
block don't just assume that the operation will not throw an exception, even if one is not listed in thethrows
clause. (You never know when an unchecked exception could be thrown.)
In the Real World
- Try to use some of the standard exception classes rather than inventing your own. This will make your program more understandable, and help it interact with other libraries.
- It is very bad practice to “eat” exceptions, meaning to silently ignore exceptions. If you catch an exception, do something with it. If you have nothing specifically to do with the exception, rethrow it or at the very least log it.
Exception Messages
Real-world applications normally have to contend with several types of messages for different purposes. The message placed in an exception when it is created is meant more for the developer, not the user. The exception usually contains a stack trace, which is of little use to a normal user. The exception message might even contain confidential information that should not be given to the end user.
This is why the types of exceptions used are so important. Normally a program would provide some friendlier message to the user, perhaps even in the user's own language if different from the application default. The Nevertheless the exception message might be useful to place in a log, to help a developer or other technical support representative track down the problem.
Thus when you catch an exception it might be useful to do several things.
- Log the exception, its message, and its stack trace. Logging is explained in a future lesson.
- Present a friendly message to the user. Providing custom messages to the user in the appropriate language is discussed in a future lesson on internationalization.
Think About It
- Is this really an error condition? Or is it just an uncommon but perfectly valid condition? Without trying to “correct” or change the input, can I process it in some valid way without creating an error? For example, I may not have expected an empty array in a
sum()
method, but isn't it valid to create a sum of zero numbers? - Do I have an appropriate
try … catch(…)
block at some level for the unchecked exceptions of the methods I call? - Did I document the unchecked exceptions I throw, using the
@throws
Javadoc tag?
Self Evaluation
- How would you determine the next line of code executed after a
throw
statement? - Everything that can be thrown using the
throw
keyword is derived from which class? - What is the difference between checked and unchecked exceptions?
- What are the two types of unchecked exceptions? What classes are they derived from?
- Is the message contained in an exception always appropriate to display to the user?
Task
Modify the Book.getYearsInPrint()
method to make sure that the answer is valid. If it yields a negative number, that means the book's publishing date was set to some date past the CURRENT_YEAR
, so throw an exception.
In the Booker method that prints all books, catch the exception representing an invalid publishing date for a single book, and inform the user that some unusual state or invalid data was encountered. Then continue printing the rest of the books.
See Also
- Exceptions (Oracle - The Java™ Tutorials)
- Catching Multiple Exception Types and Rethrowing Exceptions with Improved Type Checking (Oracle - Java™ Platform Overview)
References
- The Java® Language Specification, Java SE 11 Edition: Chapter 11. Exceptions (Oracle)
- Explanations of common Java exceptions This is a list of actual exceptions, but the descriptions are meant to be humorous and not true explanations.