Exceptions
- About the exception handling mechanism in Java
- Write try ... catch structures to catch expected exceptions
- Distinguish runtime exception classes from other exception classes
- Use finally blocks to guarantee execution of code
- Throw exceptions
- Define custom exception classes
- Understand initializer blocks
Exceptions
Exceptions are generated when a recognized condition, usually an error condition, occurs during the execution of a method
- there are a number of standard error conditions defined in Java
- you may define your own error conditions as well
When an exception is generated, it is said to be thrown
Java syntax includes a system for managing exceptions, by tracking the potential for each method to throw specific exceptions
- for each method that could throw an exception, your code must inform the Java compiler that it could throw that specific exception
- the compiler marks that method as potentially throwing that exception, and then requires any code calling the method to handle the possible exception
There are two ways to handle an exception:
- you can try the "risky" code, catch the exception, and do something about it, after which the propagation of the exception ceases
- you can mark that this method throws that exception, in which case the Java runtime engine will throw the exception back to the method that called this one (since you must inform Java that this method throws the exception, any method that calls this one must be prepared to handle the exception)
So, if you use a method in your code that is marked as throwing a particular exception, the compiler will not allow that code unless you handle the exception
Once an exception is thrown, it propagates backward up the chain of methods, from callees to callers, until it is caught
- if the exception occurs in a try block, the JVM looks to the catch block(s) that follow to see if any of them match the exception type
- the first one that matches will be executed
- if none match, then this methods ends, and execution jumps to the method that called this one, at the point the call was made
If an exception is not caught in your code (which would happen if main was marked as throwing the exception) then the JVM will catch the exception, end that thread of execution, and print a stack trace
There are some exceptions that the compiler does not enforce these rules on, those are called unchecked exceptions
Handling Exceptions
Let's say we are writing a method called getThatInt(ResultSet rs), and want to use the method getInt(int column) from the ResultSet passed in as a parameter:
public int getThatInt(ResultSet rs) {
int i = 0;
return rs.getInt(3);
}
A look at the API listing for ResultSet tells us that the getInt() method throws SQLException, so we must handle that in our code
- use try and catch
public int getThatInt(ResultSet rs) { int i = 0; try { i = rs.getInt(3); } catch (SQLException e) { System.out.println("Exception occurred!"); System.out.println(e.getMessage()); e.printStackTrace(); } return i; } - declare that the method will throw the exception and let our caller handle
it
public int getThatInt(ResultSet rs) throws SQLException { int i = 0; i = rs.getInt(3); return i; }
Note that although you are required to "handle" the exception, you aren't necessarily required to do anything useful about it!
Your decision as to which approach to use should be based on where you think responsibility for handling the exception lies - in the example above, the second approach is probably better; so that the code that works more closely with the SQL handles the exception
Exception Objects
When an exception is thrown, an exception object is created and passed to the catch block much like an parameter to a method
- occurrence of an exception generates an object (an instance of a class in the exception hierarchy) containing information about the exception
- the exception object is passed to the code designated to catch the exception, which may then use methods of the exception object to help handle the situation
There is an API class called Exception
- all exception classes inherit from Exception, which inherits from Throwable
- another class, Error, also inherits from Throwable
- your code must handle most exceptions, but generally should not attempt to handle Error subtypes (like OutOfMemoryError or StackOverflowError)
- RuntimeException is a subclass of Exception that is a base class for all the exception classes that you are not obligated to handle, but still might want to anyway (examples are ArithmeticException, from dividing by zero, NullPointerException, and ArrayIndexOutOfBoundsException)
So, there are several classes of exceptions you are not required to handle (shaded below)
- these extend either Error or RuntimeException
- the ones you are required to handle are called checked exceptions
- generally, runtime exceptions can be prevent by good coding practices:
- avoid null pointer exceptions by checking the reference first
- check array indexes before using them to avoid ArrayIndexOutOfBoundsException
- looking at the documentation for allowable parameter values, and testing them before passing them to a method will prevent IllegalArgumentException
Attempting Risky Code - try and catch
If a method is going to resolve an potential exception internally, the line of code that could generate the exception is placed inside a try block
- there may be other code inside the try block, before and/or after the risky line(s) - any code that depends upon the risky code's success should be in the try block, since it will automatically be skipped if the exception occurs
try {
code
risky code
code that depends on the risky code succeeding
}
There is usually at least one catch block immediately after the try block
- a catch block must specify what type of exception it will catch
catch (ExceptionClassName exceptionObjectName) { code using methods from exceptionObjectName }
- there can be more than one catch block, each one marked for a specific exception class
- the exception class that is caught can be any class in the exception hierarchy, either a general (base) class, or a very specific (derived) class
- the catch block(s) must handle all checked exceptions that the try block is known to throw unless you want to throw that exception back to the method that called this one
- it is possible to have a try block without any catch blocks if you have
a finally block
- but any checked exceptions still need to be caught, or the method needs to declare that it throws them
- we will cover finally later in this section
If an exception occurs within a try block, execution jumps to the first catch block whose exception class matches the exception that occurred (using an instanceof test)
- any steps remaining in the try block are skipped
If no exception occurs, then the catch blocks are skipped
- ???this would only be legal if the exception could be thrown by the method, either because it is marked to throw that exception, or because it is a RuntimeException
Note: you cannot catch an exception that would not occur in the try block
- but you can mark a method as throwing an exception that it doesn't (this leaves open that possibility that an extending class can override the method and actually throw the exception)
Notes on try ⦠catch Blocks and Variable Initialization/Scope
If declare a variable within a try block, it will not exist outside the try block, since the curly braces define the scope of the variable
- you will often need that variable later, if nowhere else other than the catch or finally blocks, so you would need to declare the variable before the try
If you declare but don't initialize a variable before a try block, and the only place you set a value for that variable is in the try block, then it is possible when execution leaves the try ... catch structure that the variable never received a value
- so, you would get a "possibly uninitialized value" error message from the compiler, since it actually keeps track of that sort of thing
- usually this happens with object references; you would also generally initialize them to null
Code Sample: Java-Exceptions/Demos/ExceptionTest.java
public class ExceptionTest {
public static void main(String[] args) {
int i, j, x = 5, y = 5, z = 0;
try {
i = x/y;
System.out.println("x/y = " + i);
j = x/z;
System.out.println("x/z = " + j);
}
catch(ArithmeticException e) {
System.out.println("Arithmetic Exception!");
}
System.out.println("Done with test");
}
}
The program will print the first result, then fail while performing the division for the second equation. Execution will jump to the catch block to print our message on the screen
Note: ArithmeticException is one of the few you are not required to catch, but you can still catch it if you wish.
Example - An Exception You Must Handle
The preceding example used a RuntimeException, which your code is not obligated to handle
Most methods in the I/O classes throw IOException, which is an exception that you must handle
Our KeyboardReader class has try and catch to handle this, essentially stifling the exception, since it is unlikely, if not impossible, to actually get an IOException from the keyboard
Code Sample: Java-Exceptions/Demos/IOExceptionTest.java
import java.io.IOException;
public class IOExceptionTest {
public static void main(String[] args) {
int num = 0;
num = System.in.read(); // comment out this line
try {
num = System.in.read();
System.out.println("You entered " + (char) num);
}
catch (IOException e) {
System.out.println("IO Exception occurred");
}
}
}
The line marked to comment out throws IOException, but is not in a try block, so the compiler rejects it. The second read attempt is within a try block, as it should be.
- try to compile this code as is, then comment out the indicated line
- there is no way we can force an IOException from the keyboard to test the catch block
Using Multiple catch Blocks
It is possible that a statement might throw more than one kind of exception
- you can list a sequence of catch blocks, one for each possible exception
- remember that there is an object hierarchy for exceptions - since the first one that matches is used and the others skipped, you can put a derived class first and its base class later (you will actually get a compiler error if you list a more basic class before a derived class, as it is "unreachable code" )
Code Sample: Java-Exceptions/Demos/MultiCatchTest.java
import util.KeyboardReader;
public class MultiCatchTest {
public static void main(String[] args) {
int num1, num2;
try {
num1 = KeyboardReader.getPromptedInt("Enter a number: ");
num2 = KeyboardReader.getPromptedInt("Enter another number: ");
System.out.println(num1 + " divided by " + num2 + " is " + num1/num2);
}
catch (NumberFormatException e) {
System.out.println("Number Format Exception occurred");
}
catch (ArithmeticException e) {
System.out.println("Divide by Exception occurred");
}
catch (Exception e) {
System.out.println("General Exception occurred");
}
}
}
The code in the try block could throw NumberFormatException during the parsing, and ArithmeticException while doing the division, so we have catch blocks for those specific cases. The more generic catch block for Exception would catch other problems, like NullPointerException.
Guaranteeing Execution of Code - the finally Block
To guarantee that a line of code runs, whether an exception occurs or not, use a finally block after the try ⦠catch blocks
The code in the finally block will almost always execute, even if an unhandled exception occurs; in fact, even if a return statement is encountered (if System.exit is called, the finally block will not execute, however)
- if an exception causes a catch block to execute, the finally block will be executed after the catch block
- if an uncaught exception occurs, the finally block executes, and then execution exits this method and the exception is thrown to the method that called this method
try {
risky code block
}
catch (ExceptionClassName exceptionObjectName) {
code to resolve problem
}
finally {
code that will always execute
}
In summary:
- a try block is followed by zero or more catch blocks
- if the catch block exception classes caught are related, the blocks must be listed in inheritance order from most derived to most basic
- there may one finally block as the last block in the structure
- there must be at least one block from the combined set of catch and finally after the try
It's possible to have a try block followed by a finally block, with no catch block
- this is used to prevent an unchecked exception from exiting the method before cleanup code can be executed
Code Sample: Java-Exceptions/Demos/FinallyTest.java
import util.KeyboardReader;
public class FinallyTest {
public static void main(String[] args) {
String name;
int age;
try {
name = KeyboardReader.getPromptedString("What is your name: ");
age = KeyboardReader.getPromptedInt("What is your age: ");
System.out.println("Hello " + name);
System.out.println("You are " + age);
}
catch (NumberFormatException e) {
System.out.println("Number Format Exception occurred");
}
catch (Exception e) {
System.out.println("General Exception occurred");
}
finally {
System.out.println("Goodbye");
}
}
}
The "Goodbye" message will always appear:
- when no exception occurs, the finally block will execute after the try
- if you force an exception by entering a non-numeric age, the catch block will execute, followed by the finally block
- ???if you enter 0 for age, the unhandled ArithmeticException will cause execution to back up the call stack, but only after executing the finally block
Important Notes on finally Blocks
As noted on the previous page, a finally block will almost always execute
- it will not execute if the try or catch blocks call System.exit()
- it will, however, run if the try or catch blocks execute a return statement
A return value from a finally block will supercede one from a try or catch block
Letting an Exception be Thrown to the Method Caller
A method that generates an exception can be written to not catch it - instead it can let it be thrown back to the method that called it
The possibility that a method may throw an exception must be defined with the method
[modifiers] returnType functionName(arguments) throws ExceptionClassName { body including risky code }
Then an instance of ExceptionClassName or a class that extends it may be thrown
- so, stating that a method throws Exception is about as generic as you can get (stating that it throws Throwable is as generic as you can get, but not recommended)
- a method can throw more than one type of exception; in which case you would use a comma-separated list of exception types
In this way, the method is now marked as throwing that type of exception, and an code that calls this method will be obligated to handle it
When you extend a class and override a method, you cannot add exceptions to the throws list
- but, a base class method can list exceptions that it does not throw, in the expectation that an overriding method will throw the exception
- this is another example of the "inheritance cannot restrict access" principle we saw earlier
If main() throws an exception, the JVM, which runs under Java rules, will handle the exception (by printing a stack trace and closing down the offending thread - in a single-threaded program, this will shut down the JVM).
Throwing an Exception
The keyword throw is used to trigger the exception-handling process (or, "raise" the exception, as it is often termed in other languages)
That word is followed by an instance of a throwable object - i.e., an instance of a class that extends Throwable
- usually, a new instance of an appropriate exception class is created to contain information about the exception
Example:
- suppose a setAge() method expects a non-negative integer - we can have it throw an IllegalArgumentException if it receives a negative value
- it makes sense for the method that calls setAge() to do something about the problem, since it is where the illegal number came from
- so, we can declare setAge() as throws IllegalArgumentException
public void setAge(int age) throws IllegalArgumentException {
if (age < 0)
throw new IllegalArgumentException("Age must be >= 0");
else
this.age = age;
}
Exercise: Payroll-Exceptions01: Handling NumberFormatException in Payroll
A though exercise - our program to this point has been prone to potential bad numeric inputs when reading from the keyboard
The parsing methods all throw NumberFormatException
We could now put each line that requests a number inside a small loop
The loop could be controlled by a boolean variable, perhaps with a name like isInvalid, and initially set to true (using the reverse approach is also a possible strategy)
- inside the loop, we try the read and parse operations
- then, still in the try block, change the state of the boolean to one that will end the loop (because we wouldn't get to that step unless we succeeded)
- in the catch block, print an error message and request to try again
- Where would you put this code? In the payroll main method or in the KeyboardReader class?
Exercise: Payroll-Exceptions01, continued
- Go ahead and add a second version of each get numeric method in KeyboardReader that accepts an error message string as a second parameter; have it loop on each numeric input request until it succeeds without throwing the exception (and printing the error message each time the exception occurs)
- Then modify Payroll to call these methods
- This approach still doesn't solve the problem of limited employee types, valid department numbers, etc. Can you think of an approach that would? (Hint: interfaces are a powerful tool ...)
Exceptions and Inheritance
If a base class method throws an exception, that behavior will also occur in any derived classes that do not override the method
An overriding method may throw the same exception(s) that the base class method threw
An overriding method cannot add new exceptions to the throws list
- similar to placing more strict access on the method, this would add restrict the derived class object in ways that a base class reference would be unaware of
If the derived class method does not throw the exception that the base class threw, it can either:
- retain the exception in the throws list, even though it does not throw it - this would enable subclasses to throw the exception
- remove the exception from its throws list, thus blocking subsequent extensions from throwing that exception
If you have a base class method that does not throw an exception, but you expect that subclasses might, you can declare the base class to throw that exception
Exception Class Constructors and Methods
There are several forms of constructors defined in the base class for the exception hierarchy
| Constructor | Description |
|---|---|
| Throwable() | Constructs a new throwable with null as its detail message. |
| Throwable(String message) | Constructs a new throwable with the specified detail message. |
| Throwable(String message, Throwable cause) | Constructs a new throwable with the specified detail message and cause. |
| Throwable(Throwable cause) | Constructs a new throwable with the specified cause and a detail message of (cause==null ? null : cause.toString()) (which typically contains the class and detail message of cause). |
The forms involving a cause are used in situations like Servlets and Java Server Pages, where a specific exception is thrown by the JSP engine, but it may be rooted in an exception from your code
- in both cases, a method in a base class is overridden by your code - since the writers of the base class did not know what specific exceptions your code might throw, and didn't want to specify something too broad like throws Exception, they settled on throws IOException, ServletException (or JSPException for Java Server Pages)
- you would try and catch for your expected exceptions, and repackage them inside ServletException objects if you did not want to handle them
A Exception object has several useful methods:
| Method | Description |
|---|---|
| getMessage() | prints the message that was associated with the exception (many of the exceptions that deal with outside resources pass on the message from the outside) - for example, when you connect to a database and run a query, that could generate an error in the database; getMessage() will show that message |
| printStackTrace() | prints to the standard error stream the trace of what function called what function, etc., leading up to the exception - there are variations of this method where you may specify a destination for the printing (note that stack trace includes the message) |
| printStackTrace(PrintStream stream) | same as above, but prints to the specified output stream (which could be hooked to a log file, for example) |
Also worth noting is that the Java Logging API has logging methods that will accept a Throwable parameter and make a log entry with the stack trace.
Creating and Using Your Own Exception Classes
You can create your own exception class by extending an existing exception class
[modifiers] NewExceptionClassName extends ExceptionClassName { create constructors that usually delegate to super-constructors }
- you could then add any fields or methods that you wish, although often that is not necessary
- you must, however, override any constructors you wish to use: Exception(), Exception(String
message), Exception(String
message, Throwable cause), Exception(Throwable cause)
- usually you can just call the corresponding super-constructor
- if you extend RuntimeException or one of its subclasses, your exception will be treated as a runtime exception (it will not be checked)
When a situation arises for which you would want to throw the exception, use the throw keyword with a new object from your exception class, for example:
throw new ExceptionClassName(messageString);
Code Sample: Java-Exceptions/Demos/NewExceptionTest.java
class NewException extends Exception {
NewException() {
super();
}
NewException(String message) {
super(message);
}
NewException(String message, Throwable cause) {
super(message, cause);
}
NewException(Throwable cause) {
super(cause);
}
}
public class NewExceptionTest {
public void thrower() throws NewException {
if (Math.random() < 0.5) {
throw new NewException("This is my exception");
}
}
public static void main(String[] args) {
NewExceptionTest t = new NewExceptionTest();
try {
t.thrower();
}
catch(NewException e) {
System.out.println("New Exception: " + e.getMessage());
}
finally {
System.out.println("Done");
}
}
}
The thrower method randomly throws a NewException, by creating and throwing a new instance of NewException
main tries to call thrower, and catches the NewException when it occurs
Exercise: Payroll-Exceptions02
Our payroll program can now handle things like a bad numeric input for pay rate (valid format, but not sensible, like a negative number) in a more comprehensive manner
- we already are checking the numeric inputs from the keyboard, but there is no guarantee that later code will remember to do this - using an exception mechanism guarantees protection from invalid values
- In the util package, create an exception class for InvalidValueException
- Note that the Java API already contains a class for this purpose, IllegalArgumentException, but it is a RuntimeException - we would like ours to be a checked exception
- In Employee (and potentially its subclasses), change the constructors that accept pay rate and the setPayRate methods to now throw that exception (a question to ask yourself - is it necessary to actually test the pay rate value anywhere other than in the Employee class setPayRate method?)
- For any code that calls those constructors/methods, choose an appropriate approach to dealing with the potential exception
Rethrowing Exceptions
An exception may be rethrown
When we throw an exception, it does not necessarily have to be a new object
- we can reuse an existing one
This allows us to partially process the exception and then pass it up to the method that called this one to complete processing
- this is often used in servlets and JSPs to handle part of the problem (possibly just log it), but then pass the problem up to the servlet or JSP container to abort and send an error page
String s = "1234X";
try {
Integer.parseInt(s);
} catch (NumberFormatException e) {
System.out.println("Bad number, passing buck to JVM");
throw e;
}
The stack trace will still have the original information
- the fillInStackTrace method for the exception object will replace the original information with information detailing the line on which fillInStackTrace was called as the origin of the exception
Initializer Blocks
Class properties that are object types can be initialized with a newly constructed object
public class MyClass {
private Random rand = new java.util.Random();
private MegaString ms = new MegaString("Hello " + rand.nextInt(100));
private int x = rand.nextInt(100);
. . .
}
- the MegaString class constructor code will run whenever a MyClass object is instantiated
But what if the object's constructor throws an exception?
class MegaString{
public MegaString(String s) throws Exception {
. . .
}
}
- the MyClass code won't compile - you cannot put a property declaration into a try ... catch structure and there is no place to state that the property declaration throws an exception
You can use an initializer block to handle this situation
public class MyClass {
private java.util.Random rand = new java.util.Random();
private MegaString ms = null;
{
try { ms = new MegaString()"Hello " + rand.nextInt(100); }
catch (Exception e) { . . . }
}
private int x = rand.nextInt(100);
. . .
}
This is not absolutely necessary, since the initialization could be done in a constructor, where a try ... catch would be legal
- but then it would need to be done in every constructor, which someone adding another constructor later might forget
Initializers are run in the order in which they appear in the code, whether standalone initializers, or initializers in a field declaration so, in the above code:
- the Random object gets created for the first field
- the MegaString gets the first generated random number
- x gets the second generated random number
Static Initializer Blocks
If a property is static, and is populated with a newly constructed object, that object's constructor code will run when the class loads
- in our example, if we make the MegaString property static, its constructor will run when the class loads
public class MyClass {
private static MegaString sms = new MegaString("Goodbye");
. . .
}
- again, this won't compile, but now there is no way even to defer the issue to the constructors, since the element is static
You can use a static initializer block to handle this problem
public class MyClass {
private static MegaString sms = null;
static {
try { sms = new MegaString("Hello"); }
catch (Exception e) { . . . }
}
. . .
}
Again, the initializers are run in the order in which they appear in the code, when the class is loaded.
Assertions
Java 1.4 added the concept of assertions, code lines that test that a presumed state actually exists
- if the state is not as presumed, then an AssertionError will be thrown
- assertions are not intended for testing values that could be expected; they are intended to be used when it is believed that a state exists, but we are not absolutely sure we have covered all the possible avenues that affect the state
To use an assertion in code:
assert (condition)[: messageExpression];
The optional messageExpression will be converted to a String and passed as the message to the AssertionError constructor, so it cannot be a call to a function declared as void
For example, perhaps we are using a third-party function that is specified to return a double value between 0 and 1, but we'd like to guarantee that is the case
Code Sample: Java-Exceptions/Demos/AssertionTest.java
public class AssertionTest {
public static void main(String[] args) {
double value = thirdPartyFunction();
assert (value >= 0 && value < 1) :
" thirdPartyFunction value " + value + " out of range";
System.out.println("Value is " + value);
}
public static double thirdPartyFunction() {
return 5.0;
}
}
If the function returns a value outside of our expected range, like 5.0, the assertion condition will evaluate to false, so an AssertionError will be thrown with the message "thirdPartyFunction value 5.0 out of range"
Note: in a 1.4 compiler, you must inform the compiler that you are compiling your code under Java 1.4 rules to use assertions, using the -source 1.4 command line switch:
javac -source 1.4 ClassName.java
In Java 5 and later this is not necessary.
Assertions are used for debugging a program, and usually not enabled for production runs
- you must specifically enable assertions when you run the program, by using the command line switch -enableassertions (or -ea)
java -enableassertions ClassName java -ea ClassName
Sun's "rules" for assertions emphasize that they will be disabled most of the time:
- they should not be used for checking the values passed into public methods, since that check will disappear if assertions are not enabled
- the assertion code shouldn't have any side effects required for normal operation
Exceptions Conclusion
In this lesson of the Java tutorial you have learned:
- how Java's exception handling mechanism works
- how to try and catch exceptions
- about the various types of checked and unchecked exceptions
- how to write exception classes
- how to throw exceptions
- how to use assertions