Exceptions and Throws Clauses


Errors

There are a lot of errors that a program can encounter. For example, it might try to compute "cat".charAt(5), which cannot be done because string "cat" does not have a character at index 5.

The most obvious thing for a program to do in situations like that is to stop running and to tell you where the error occurred. For experimental programs such as you are writing, that is probably reasonable. But for production quality programs, such as those produced by software firms, that is not acceptable. The user is probably not the person who wrote the program, and does not want to know what the error was or where it occurred. He or she would prefer for the program to recover from the error, if possible.

There are other kinds of errors that you would like a program to recover from. For example, we will shortly see how a program can open a file and read its contents. But what if the file that the program tries to open does not exist? That should not necessarily stop the program. A better idea is for the program to try something else instead.


Throwing and catching exceptions

Java has a collection of objects called exceptions, each indicating a reason why something went wrong. For example, one of the exceptions is called StringIndexOutOfBoundsException, indicating that an index used in charAt does not exist in a string.

When an error occurs, a program throws an exception object, with the idea that the exception will be caught someplace else, where the program knows how to recover from the error. To throw exception StringIndexOutOfBoundsException, you would use statement

  throw new StringIndexOutOfBoundsException();

To catch an exception, use a try statement. A simple form is

  try
  {
    ...statements where an exception might be thrown
  }
  catch(Exception e)
  {
    decide what to do if exception e is thrown
  }
If the compound statement that follows try does not throw an exception then the catch part is not done. But if an error is thrown, then the catch part is started immediately. Inside the compound statement that follows the catch line, variable e refers to the exception object that was thrown. You might want to show the exception. Use e.toString() as a string that describes the error. So the catch part might say
  catch(Exception e)
  {
    System.out.println(e.toString());
  }

You can have more than one catch part. Each has a kind of exception that it can catch. Kind Exception stands for any kind of exception. But you can also use more specific exceptions. For example, the following tries to make c be the 10th character of a string that is produced by method getStr( ). (Method getStr is just a made up method. It could be anything.) If that character does not exist, then it prints a message and makes c be a blank instead. If some other kind of exception is thrown, then it prints a description of the exception and makes c be an asterisk.

  char c;
  try
  {
    c = getStr().charAt(10);
  }
  catch(StringIndexOutOfBoundsException e)
  {
    System.out.println("Index does not exist, using blank");
    c = ' ';
  }
  catch(Exception e)
  {
    System.out.println("Could not get string at all: " + e.toString());
    c = '*';
  }

Context

A try statement is a context. Any variable that is created inside it only exists as long as the program stays inside it. Notice that variable c above is created outside of the try statement and given a value inside the try statement. That is essential. Writing

  try
  {
    char c = getStr().charAt(10);
  }
  catch(StringIndexOutOfBoundsException e)
  {
    System.out.println("Index does not exist, using blank");
    char c = ' ';
  }
  catch(Exception e)
  {
    System.out.println("Could not get string at all: " + e.toString());
    char c = '*';
  }
does not work since variable c cannot be used after the try statement is done.


Throwing exceptions out of methods

Sometimes a method uses an expression or statement that might throw an exception, but the method does not catch that exception. When the exception is thrown, the method will stop running immediately and ask the method that called it to catch the exception. If that method does not catch the exception, then the caller's caller is asked to catch it, and so on. If no method catches the exception, the Java run-time system reports an uncaught exception.

If a method can throw an exception (possibly in a method that it calls) that it does not catch then you are required to say so just after the method heading. Write throws E to indicate that this method might cause but not catch an exception of type E. For example, suppose that you do not like the fact that the charAt method numbers from 0. So you write your own method that does the same thing, but that numbers the characters starting at 1, so that index(s, 1) is the first character of string s. Here is an attempt.

  public static char index(String s, int n)
  {
    return s.charAt(n-1);
  }
But the java compiler complains about that, and will not compile it. The problem is that expression s.charAt(n-1) can throw exception StringIndexOutOfBoundsException, and you have not said so. A correct definition is as follows.
  public static char index(String s, int n)
    throws StringIndexOutOfBoundsException
  {
    return s.charAt(n-1);
  }
You are required to list all of the exceptions that the method can throw. Just separate them by commas. If you are not concerned with being precise, you can say that a method throws Exception, meaning it can throw any kind of exception. So it would also work to write
  public static char index(String s, int n)
    throws Exception
  {
    return s.charAt(n-1);
  }
but that is considered an imprecise way to write this method definition, not suitable for serious software.

A special kind of exceptions called error exceptions do not need to be mentioned in throws clauses. For example, method

  public static void error(String message)
  {
    throw new Error(message);
  }
throws an error exception. (If message is "help!" and the thrown error exception is e, then e.toString( ) is "help!".) Notice that there is no throws clause. It is not required for an error exception.


More information

Exceptions are discussed in Chapter 9 of Savitch and Carrano. That chapter relies on some fairly advanced material in inheritance that we have not discussed.


Problems

  1. [solve] What is exception handling useful for?

  2. [solve] What kind of statement is used to catch an exception?

  3. [solve] What kind of statement is used to throw an exception?

  4. [solve] Write a definition of a method called fifth that returns fifth character in a given string, and that throws StringIndexOutOfBoundsException if there is no fifth character. (The fifth character of "abcdefg" is 'e'.)


Summary

Exceptions give you two capabilities.

  1. A program can recover from error situations using a try statement.

  2. When a program hits a place where it does not know what to do, it can throw an exception.

Remember that, if a method can throw an exception that it does not catch, it requires a throws clause after its heading, unless the exception is an error exception.