MENU
Time well spent!!! Connie was great!More Testimonials »

Exceptions

In this lesson, you will learn about error handling within Java.

Lesson Goals

  • Learn about the exception handling mechanism in Java.
  • Write try ... catch structures to catch expected exceptions.
  • Distinguish runtime exception classes from other exception classes.
  • Learn how to use finally blocks to guarantee execution of code.
  • Learn about thrown exceptions.
  • Define custom exception classes.
  • Learn about 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, and 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. Note that:

  • 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 the method with the risky statement indcating that it throws an exception. Any statement that calls a method capable of throwing an exception must deal with it.

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. Note that:

  • 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 method ends, and execution jumps to the method that called this one, at the point where 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 cases where the compiler does not enforce these rules. Exceptions that fit this category are called unchecked exceptions.

Handling Exceptions

Let's say we are writing a method called getThatInt(ResultSet rs) and we 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

  1. 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;
    }
  2. 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 a parameter to a method. Note that:

  • 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. Note that:

  • 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 in the image below). Note that:

  • These extend either Error or RuntimeException.
  • The ones you are required to handle are called checked exceptions.
  • Generally, runtime exceptions can be prevented 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.

Exception Hierarchy

Attempting Risky Code - try and catch

If a method is going to resolve a 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 {
	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 (xceptionClassName 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. The catch blocks will also be skipped if an exception that is not caught occurs, such as a RuntimeException, or an exception that the method declared it throws.

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 the possibility that an extending class can override the method and actually throw the exception).

Notes on try ... catch Blocks and Variable Scope/Initialization

If you 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 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 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.

  • Ttry to compile this code as is and 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 + " / " + num2 + " = " + 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.

  • 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.
  • If either the try block or a catch block executes a return statement, the finally block executes before we leave the method.
  • If either the try block or a catch block calls System.exit, the finally block will not execute.
  • For the sake of completeness, if a finally block executes a return while an uncaught exception is pending, the exception is stifled; that is, it just disappears.
try {
	risky code block
}
catch (ExceptionClassName exceptionObjectName) {
	code to resolve problem
}
finally {
	code that will always execute
}

In summary, note the following:

  • 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, or an exception the method declared it throws, 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) {
    System.out.println("Returned value is " + go());
  }
  
  public static int go() {
    int choice = 0;
    try {
      String name = KeyboardReader.getPromptedString("Enter your name: ");
      System.out.println("MENU:");
      System.out.println("1 - normal execution");
      System.out.println("2 - uncaught ArithmeticException");
      System.out.println("3 - return from try block");
      System.out.println("4 - call System.exit");
      System.out.println(
            "5 - return 5 from finally after ArithmeticException");
      System.out.println(
            "6 - return 6 from finally after try returns -1");
      System.out.println("X - catch NumberFormatException");
      choice = KeyboardReader.getPromptedInt("Enter your choice: ");

      if (choice == 1) System.out.println("Hello " + name);
      else if (choice == 2) System.out.println("1 / 0 =  " + 1/0);
      else if (choice == 3) return 3;
      else if (choice == 4) System.exit(1);
      else if (choice == 5) System.out.println("1 / 0 =  " + 1/0);
      else if (choice == 6) return -1;
    }
    catch (NumberFormatException e) {
      System.out.println("Number Format Exception occurred");
    }
    finally {
      System.out.println("Goodbye from finally block");
      if (choice == 5) return 5;
      if (choice == 6) return 6;
    }
    return 0;
  } 
}

The program shows a menu of possible execution paths you can trigger. The "Goodbye from finally block " message will always appear except from an explicit call to System.exit in the try block:

  • When no exception occurs, the finally block will execute after the try.
  • If an uncaught exceptions occurs, like when we divide by zero, the finally block will execute before we are thrown out of the method.
  • If we execute a return from the try block, the finally block still executes before we leave.
  • Catching a NumberFormatException is still followed by executing the finally block.
  • But, calling System.exit in the try block causes the JVM to shut down without executing the finally block.
  • When we force an uncaught exception but return from the finally block, we do not get a stack trace, indicating that the ArithmeticException just disappeared (compare choices 2 and 5).
  • When both the try block and the finally block execute a return statement, the value from the finally block is the one actually returned.

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 Throwableis 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 a 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.

For example, suppose a setAge() method expects a nonnegative 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;
}

Payroll-Exceptions01: Handling NumberFormatException in Payroll

Duration: 5 to 10 minutes.

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.
  1. Where would you put this code? In the payroll main method or in the KeyboardReader class?

Solution:

As a general principle, tools shouldn't attempt to handle exceptions when the handling logic would vary depending on the code using the tool. But, it would be a tremendous burden to put each step of a program that requests a numeric input in a looped try/catch block.

Instead, we could recognize that a common approach would be to loop until the input is numeric, printing an error message each time.

We could overload the get methods in KeyboardReader to accept an error message string, so it could do the looping for us. This way a reasonable solution would be provided, but the original method would still be available if the programmer wants to customize the exception handling.

If a programmer wants a different approach, they are still free to write it in their code and use the original KeyboardReader methods .

Payroll-Exceptions01, continued

Duration: 15 to 20 minutes.
  1. 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).
  2. Then modify Payroll to call these methods.

Challenge

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 ...).

Solution:

Solutions/Payroll-Exceptions01/util/KeyboardReader.java
package util;
import java.io.*;

public class KeyboardReader {


---- C O D E   O M I T T E D ----
  public static int getPromptedInt(String prompt, String errMsg) {
    for ( ; ; ) {
      try {
    		return Integer.parseInt(getPromptedString(prompt));
      } catch (NumberFormatException nfe) {
        System.out.println(errMsg);
      }
    }
  }
  
  public static float getPromptedFloat(String prompt, String errMsg) {
    for( ; ; ) {
      try {
        return Float.parseFloat(getPromptedString(prompt));
      } catch (NumberFormatException nfe) {
        System.out.println(errMsg);
      }
    }
  }

  public static double getPromptedDouble(String prompt, String errMsg) {
    for( ; ; ) {
      try {
        return Double.parseDouble(getPromptedString(prompt));
      } catch (NumberFormatException nfe) {
        System.out.println(errMsg);
      }
    }
  }
}

Solution:

Solutions/Payroll-Exceptions01/Payroll.java
import employees.*;
import vendors.*;
import finance.*;
import util.*;

public class Payroll {  
  public static void main(String[] args) {
    Employee[] e = new Employee[5];
    String fName = null;
    String lName = null;
    int dept = 0;
    double payRate = 0.0;
    double hours = 0.0;

    for (int i = 0; i < e.length; i++) {
      char type = KeyboardReader.getPromptedChar(
          "Enter type: E, N, or C: ");
      if (type != 'e' && type != 'E' && 
          type != 'n' && type != 'N' && 
          type != 'c' && type != 'C') {
        System.out.println("Please enter a valid type");
        i--;
        continue;        
      }
      fName = KeyboardReader.getPromptedString("Enter first name: ");
      lName = KeyboardReader.getPromptedString("Enter last name: ");
      dept = KeyboardReader.getPromptedInt(
          "Enter department: ", "Department must be numeric");
      do {
        payRate = KeyboardReader.getPromptedDouble(
            "Enter pay rate: ", "Pay rate must be numeric");
        if (payRate < 0.0) System.out.println("Pay rate must be >= 0");
      } while (payRate < 0.0);

      switch (type) {
        case 'e':
        case 'E': e[i] = new ExemptEmployee(fName, lName, dept, payRate);
                  break;
        case 'n':
        case 'N': do {
                    hours = KeyboardReader.getPromptedDouble(
                        "Enter hours: ", "Hours must be numeric");
                    if (hours < 0.0) 
                      System.out.println("Hours must be >= 0");
                  } while (hours < 0.0);
                  e[i] = new NonexemptEmployee(
                      fName, lName, dept, payRate, hours);
                  break;
        case 'c':
        case 'C': do {
                    hours = KeyboardReader.getPromptedDouble(
                        "Enter hours: ", "Hours must be numeric");
                    if (hours < 0.0) 
                      System.out.println("Hours must be >= 0");
                  } while (hours < 0.0);
                  e[i] = new ContractEmployee(
                      fName, lName, dept, payRate, hours);
      }

      System.out.println(e[i].getPayInfo());
    }

---- C O D E   O M I T T E D ----
  }

}

The revised code uses the new overloads of the getPromptedXXX methods.

Challenge Solution:

Solutions/Payroll-Exceptions01-challenge/util/IntValidator.java
package util;

public interface IntValidator {
	public boolean accept(int candidate);
}

This interface specifies a method that will be used to validate integers. A validator for a specific field (like department) would implement this with code to test for legal values for that field. The package contains similar interfaces for floats and doubles.

Challenge Solution:

Solutions/Payroll-Exceptions01-challenge/employees/DeptValidator.java
package employees;
import util.IntValidator;

public class DeptValidator implements IntValidator {

	@Override
	public boolean accept(int dept) {
		return dept > 0 && dept <= 5;
	}

}
/* Sample usage in Payroll

dept = KeyboardReader.getPromptedInt(
"Enter department: ", "Dept must be numeric",
new DeptValidator(), "Valid depts are 1 - 5");

*/

This class validates department numbers to be from 1 - 5 inclusive. We also could create separate validators for pay rates, etc.

Challenge Solution:

Solutions/Payroll-Exceptions01-challenge/util/KeyboardReader.java
package util;
import java.io.*;

public class KeyboardReader {


---- C O D E   O M I T T E D ----
	public static int getPromptedInt(String prompt) {
		return Integer.parseInt(getPromptedString(prompt));
	}
	
	public static int getPromptedInt(String prompt, String errMsg) {
		for ( ; ; ) {
			try {
				return Integer.parseInt(getPromptedString(prompt));
			} catch (NumberFormatException e) {
				System.out.println(errMsg);
			}
		}
	}
	public static int getPromptedInt(
			String prompt, String formatErrMsg, 
			IntValidator val, String valErrMsg) {
		for ( ; ; ) {
			try {
				int num = Integer.parseInt(getPromptedString(prompt));
				if (val.accept(num)) return num;
				else System.out.println(valErrMsg);
			} catch (NumberFormatException e) {
				System.out.println(formatErrMsg);
			}
		}
	}
	
	public static float getPromptedFloat(String prompt) {
		return Float.parseFloat(getPromptedString(prompt));
	}
	public static float getPromptedFloat(String prompt, String errMsg) {
		for ( ; ; ) {
			try {
				return Float.parseFloat(getPromptedString(prompt));
			} catch (NumberFormatException e) {
				System.out.println(errMsg);
			}
		}
	}
	public static float getPromptedFloat(
			String prompt, String formatErrMsg, 
			FloatValidator val, String valErrMsg) {
		for ( ; ; ) {
			try {
				float num = Float.parseFloat(getPromptedString(prompt));
				if (val.accept(num)) return num;
				else System.out.println(valErrMsg);
			} catch (NumberFormatException e) {
				System.out.println(formatErrMsg);
			}
		}
	}

	public static double getPromptedDouble(String prompt) {
		return Double.parseDouble(getPromptedString(prompt));
	}
	public static double getPromptedDouble(String prompt, String errMsg) {
		for ( ; ; ) {
			try {
				return Double.parseDouble(getPromptedString(prompt));
			} catch (NumberFormatException e) {
				System.out.println(errMsg);
			}
		}
	}
	public static double getPromptedDouble(
			String prompt, String formatErrMsg, 
			DoubleValidator val, String valErrMsg) {
		for ( ; ; ) {
			try {
				double num = Double.parseDouble(getPromptedString(prompt));
				if (val.accept(num)) return num;
				else System.out.println(valErrMsg);
			} catch (NumberFormatException e) {
				System.out.println(formatErrMsg);
			}
		}
	}
}

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 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:

  1. Retain the exception in the throws list, even though it does not throw it; this would enable subclasses to throw the exception.
  2. 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] class 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.

Payroll-Exceptions02

Duration: 20 to 30 minutes.

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.

  1. 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.
  2. 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?). You will see that the effect of throwing the exception ripples through a lot of code.
  3. For any code that calls those constructors/methods, choose an appropriate approach to dealing with the potential exception.
  4. The solution uses the validators from the previous challenge exercise, it will work without that feature, but feel free to add that logic into your code as well.

Solution:

Solutions/Payroll-Exceptions02/util/InvalidValueException.java
package util;
public class InvalidValueException extends Exception {

  public InvalidValueException() { super(); }
  public InvalidValueException(String message) { super(message); }
  public InvalidValueException(Throwable cause) { super(cause); }
  public InvalidValueException(String message, Throwable cause) {
    super(message, cause);
  }

}

Solution:

Solutions/Payroll-Exceptions02/employees/Employee.java
package employees;
import finance.Payable;
import util.*;

public class Employee extends Person implements Payable {

---- C O D E   O M I T T E D ----
  public Employee(String firstName, String lastName, double payRate) 
				throws InvalidValueException {
    super(firstName, lastName);
    setPayRate(payRate);
  }
  public Employee(String firstName, String lastName,
		int dept, double payRate)
				throws InvalidValueException {
    this(firstName, lastName, dept);
    setPayRate(payRate);
  }

---- C O D E   O M I T T E D ----
  public void setPayRate(double payRate) throws InvalidValueException {
    DoubleValidator val = new ValidatePayRate();
    if (!val.accept(payRate))
      throw new InvalidValueException(payRate + " is an invalid pay rate");
    this.payRate = payRate;
  }


---- C O D E   O M I T T E D ----
}

The marking of setPayRate throws InvalidValueException ripples through the constructors, so they should be marked as well.

Solution:

Solutions/Payroll-Exceptions02/employees/ExemptEmployee.java
package employees;

import util.*;

public class ExemptEmployee extends Employee {
  
  public ExemptEmployee() {
  }
  public ExemptEmployee(String firstName, String lastName) {
    super(firstName, lastName);
  }
  public ExemptEmployee(String firstName,String lastName, int dept) {
    super(firstName, lastName, dept);
  }
  public ExemptEmployee(String firstName, String lastName, double payRate)
				throws InvalidValueException {
    super(firstName, lastName, payRate);
  }
  public ExemptEmployee(String firstName, String lastName, 
  			int dept, double payRate)
				throws InvalidValueException {
    super(firstName, lastName, dept, payRate);
  }  
  public String getPayInfo() {
    return "Exempt Employee " + getId() + " dept " + getDept() + 
           " " + getFirstName() + " " + getLastName() + 
           " paid " + getPayRate();
  }
}

Calling super-constructors that throw our exception requires that these constructors also be marked. The other classes, not shown, should be similarly marked.

Solution:

Solutions/Payroll-Exceptions02/Payroll.java
import employees.*;
import vendors.*;
import util.*;
import finance.*;

public class Payroll {	
	public static void main(String[] args) {

---- C O D E   O M I T T E D ----

		for (int i = 0; i < e.length; i++) {
			try {

---- C O D E   O M I T T E D ----

				do {
					payRate = KeyboardReader.getPromptedDouble("Enter pay rate: ", 
									"Pay rate must be numeric");
					if (payRate < 0.0) System.out.println("Pay rate must be >= 0");
				} while (payRate < 0.0);
	
				switch (type) {
					case 'e':
					case 'E': e[i] = new ExemptEmployee(fName, lName, dept, payRate);
										break;
					case 'n':
					case 'N': do {
											hours = KeyboardReader.getPromptedDouble(
														"Enter hours: ", "Hours must be numeric");
											if (hours < 0.0) 
												System.out.println("Hours must be >= 0");
										} while (hours < 0.0);
										e[i] = new NonexemptEmployee(fName, lName, dept, 
													payRate, hours);
										break;
					case 'c':
					case 'C': do {
											hours = KeyboardReader.getPromptedDouble(
														"Enter hours: ", "Hours must be numeric");
											if (hours < 0.0) 
												System.out.println("Hours must be >= 0");
										} while (hours < 0.0);
										e[i] = new ContractEmployee(fName, lName, dept, 
													payRate, hours);
				}

---- C O D E   O M I T T E D ----

			} catch (InvalidValueException ex) {
					System.out.println(ex.getMessage());
					i--;	//failed, so back up counter to repeat this employee
				}
		}

---- C O D E   O M I T T E D ----

	}
}

Since we are already checking the values of the pay rate and hours, we shouldn't expect to see any exceptions thrown, so it is reasonable to put the entire block that gets the employee data and creates an employee in a try block. If we decrement the counter upon a failure, then that employee's data will be requested again.

You might want to test your logic by temporarily changing one of the test conditions you use when reading input (like hours > 0 to hours > -20), so that you can the result (keep count of how many employees you are asked to enter).

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:

  1. The Random object gets created for the first field.
  2. The MegaString gets the first generated random number.
  3. x gets the second generated random number.

Static Initializer Blocks

If a field 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

Oracle'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.

Collections → ← Interfaces

Client Success
  1. Compare Us
  2. Client List
  3. Testimonials
Join The Team
  1. Learn how you can become a Webucator Trainer
  2. Career Opportunities
Locations
© Webucator, Inc. All rights reserved. |Toll Free: 1-877-932-8228Toll Free: 1-877-932-8228 |Outside the USA: 315-849-2724|Fax: 315-849-2723