slanted W3C logo

Day 34 — Exceptions

Recap

Java uses both checked and unchecked exceptions. The programmer must deal with checked exceptions, but is allowed to ignore unchecked exceptions.

Unchecked exceptions include Error, RuntimeException and all of their subclasses. All other exceptions are checked exceptions.

Recap: Handling Exceptions

An exception handler in Java has a try-catch structure:

      Integer i = null;
      try
      {
         i = Integer.parseInt(someString);
      }
      catch (NumberFormatException ex)
      {
         output.println("The entered string was not an integer");
      }

Handling Multiple Exceptions

It is common to have a block of code that contains many method invocations that could throw exceptions. Also, many methods can throw more than one kind of exception. In such circumstances, you can use more than one catch block.

You need to remember three things when using multiple catch blocks:

  1. The catch blocks are scanned in the order that they appear, and the first block that can handle the thrown exception is chosen as the handler.
  2. You cannot catch a checked exception that is not thrown by any of the methods in the try block. Doing so leads to a compiler error.
  3. All catch blocks must be reachable; if not, then the compiler will issue an error.

1. catch blocks are scanned in order

Lab 11.1 (page 422) of the textbook introduces client-server (network) programming. Network programming involves IO that occurs over a network connection. The IO occurs via a socket. Creating a socket in Java requires that the programmer deal with two checked exceptions:

public Socket(String host, int port)
   throws UnknownHostException, IOException

Creates a stream socket and connects it to the specified port number on the named host.

Socket Exceptions


Handling Socket Exceptions

Because UnknownHostException is substitutable for IOException, you could use one catch block that catches IOException; however, you might want to deal with the exceptions in different ways. Catching both UnknownHostException and IOException gives you this opportunity.

      // complete client and server programs at the end of slides
      
      Socket socket = null;
      boolean done = false;
      while (socket == null && !done)
      {
         try
         {
            output.println();
            output.print("Server IP (\"done\" to quit): ");
            String serverName = input.next().trim();
            if (serverName.equalsIgnoreCase("done"))
            {
               done = true;
            }
            else
            {
               output.print("Port number: ");
               Integer port = Integer.parseInt(input.next().trim());
               socket = new Socket(serverName, port);
               output.println("Connected to server");
               // more code here...
            }
         }
         catch (UnknownHostException ex)
         {
            output.println(ex);
            output.println("Check the hostname");
         }
         catch (IOException ex)
         {
            output.println(ex);
            output.println("Check the hostname and port number");
            output.println("Make sure the server is running");
         }
         catch (NumberFormatException ex)
         {
            output.println("Please enter an integer port number.");
         }
      }

2. Cannot catch checked exceptions that cannot be thrown

Java requires that a method must indicate that it throws a checked exception in the method header:

public Socket(String host, int port)
   throws UnknownHostException, IOException

As a programmer, you must deal with all checked exceptions, but you cannot include a handler for an checked exception that is not thrown:

      try
      {
         socket = new Socket(someHostName, somePortNumber);
      }
      catch (UnknownHostException ex)
      {
         // ...
      }
      catch (IOException ex)
      { 
         // ...
      }
      // compiler error
      catch (SQLException ex)
      { }

Note that Java does not require unchecked exceptions to appear in the method header so there is no way for the compiler to verify if a method throws an unchecked exception; thus, you can catch whatever unchecked exception you want, even if the code in the try block can never cause such an exception to occur.

3. All catch blocks must be reachable

Substitutability affects the order in which you must place the catch blocks. If one type of exception you want to catch is a subclass of another type of exception you want to catch, then the subclass exception handler must appear before the superclass exception handler.

      try
      {
         socket = new Socket(someHostName, somePortNumber);
      }
      // compiler error;
      // UnknownHostException handler is unreachable
      catch (IOException ex)
      {
         // ... 
      }
      catch (UnknownHostException ex)
      {
         // ... 
      }

The problem in the example above is that UnknownHostException is a subclass of IOException. If an UnknownHostException is thrown, it will always be caught by the IOException (because UnknownHostException is substitutable for IOException).

We say that the UnknownHostException handler is unreachable, and the compiler will issue a compilation error.

Summary of Unchecked Exceptions

An unchecked exception is an exception that the programmer does not have to catch; they are generally caused by programming errors or events outside the control of the program. Unchecked exceptions may appear under the "Throws" section of a method's API, but it does not appear in the method header. Unchecked exception objects are instances of Error, RuntimeException, or one of their subclasses.

Summary of Checked Exceptions

An checked exception is an exception that the programmer must catch*; they are events that any well-written program should anticipate and deal with. Checked exceptions appear under the "Throws" section of a method's API, and must appear in the method header. Checked exception objects are instances Exception, or one of its subclasses (other than RuntimeException).

eCheck11A

You are supposed to solve eCheck11A without using any control statements (like if or switch).

The goal is to interpret a user-supplied comma delimited string as the time of day, and compute the number of minutes since midnight. There are also 4 possible error conditions that you have to check for.

InputMinutes since midnightError message
12:10:am 10
1:25:PM 805
15:00:am Values out of range!
3:30pm Missing colon!
12:fifteen:am Non-numeric data!
7:59:TM Invalid AM/PM indicator!

eCheck11A

The 4 error conditions are detected by catching exceptions.

Missing colon:
Use a StringTokenizer with ":" as the delimiter. Ask the tokenizer for 3 tokens (hours, minutes, am/pm) and catch NoSuchElementException.

Non-numeric data:
Catch NumberFormatException

Invalid AM/PM indicator:
Use Step 7 in the question and catch IndexOutOfBoundsException.

Values out of range:
?

To Do For Next Lecture

Finish reading Chapter 11 if you haven't done so already.

The Client Program

From Lab 11.1 of the textbook.

import java.net.*;
import java.io.*;
import java.util.*;

public class Client
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      Scanner input = new Scanner(System.in);
      
      Socket socket = null;
      boolean done = false;
      while (socket == null && !done)
      {
         try
         {
            output.println();
            output.print("Server IP (\"done\" to quit): ");
            String serverName = input.next().trim();
            if (serverName.equalsIgnoreCase("done"))
            {
               done = true;
            }
            else
            {
               output.print("Port number: ");
               Integer port = Integer.parseInt(input.next().trim());
               socket = new Socket(serverName, port);
               output.println("Connected to server");
               output.println();
               
               PrintStream out = new PrintStream(socket.getOutputStream());
               InputStreamReader r = new InputStreamReader(socket.getInputStream());
               BufferedReader in = new BufferedReader(r);
               
               output.println("Enter an integer [ENTER=sentinel]");
               output.print("> ");
               String request = input.nextLine().trim();
               request = input.nextLine().trim();
               for(; request.length() != 0; request = input.nextLine())
               {
                  out.println(request);
                  output.println(in.readLine());
                  output.print("> ");
               }
               socket.close();
            }
         }
         catch (UnknownHostException ex)
         {
            output.println(ex);
            output.println("Check the hostname");
         }
         catch (IOException ex)
         {
            output.println(ex);
            output.println("Check the hostname and port number");
            output.println("Make sure the server is running");
         }
         catch (NumberFormatException ex)
         {
            output.println("Please enter an integer port number.");
         }
         catch (IndexOutOfBoundsException ex)
         {
         }
      }
      
   }
}

The Server Program

From Lab 11.1 of the textbook.

import java.net.*;
import java.io.*;
import java.util.*;

public class Server
{
   public static void main(String[] args)
   {
      PrintStream output = System.out;
      
      final int PORT = 4413;
      
      try
      {
         ServerSocket serverSocket = new ServerSocket(PORT);
         Socket socket = serverSocket.accept();
         output.println();
         output.println("Accepted connection from " + socket.getInetAddress());
         output.println("Waiting for an integer");
         
         PrintStream out = new PrintStream(socket.getOutputStream(), true);
         InputStreamReader r = new InputStreamReader(socket.getInputStream());
         BufferedReader in = new BufferedReader(r);
         
         String request = in.readLine();
         for(; request != null; request = in.readLine())
         {
            int i = 0;
            try
            {
               i = Integer.parseInt(request);
            }
            catch (NumberFormatException ex)
            {}
            out.println(Math.sqrt(i));
         }
         socket.close();
         serverSocket.close();
      }
      catch (IOException ex)
      {
         output.println(ex);
      }
   }
}