Java Tutorials Made Easy banner

  

Section 1 - Exceptions Defined

 

Suppose you wrote a program that prints full names from a list, and capitalizes those whose last name starts with the letter ‘A’ through ‘L’.

 

Listing 1 - UpperCaseIncorrect.java 

 1: public class UpperCaseIncorrect 
 2: {
 3:    public static void main(String[] args)
 4:    {
 5:        String[] names = {"Susan Porter", "Javier Jones",
 6:                          "Mickey"};
 7:        for (String name : names)
 8:        {
 9:            String lastName = name.substring(name.indexOf(' '));
10:            if (lastName.charAt(1) >= 'M') 
11:                System.out.println(name);
12:            else 
13:                System.out.println(name.toUpperCase());
14:        }
15:    }
16: }

The program assumes that each full name in the list consists of a first name and a last name separated by a whitespace character. However, the last element in the list consists of only the first name “Mickey”. The program produces the following output.

 

OUTPUT:

Susan Porter

JAVIER JONES

Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -1

at java.lang.String.substring(String.java:1927)

at UpperCaseIncorrect.main(UpperCaseIncorrect.java:9)

 

The first two full names are printed as expected, then an exception occurs which causes the program to terminate after displaying text describing what went wrong. The name “Mickey” is not displayed.

An exception is an error that can occur in your program. Errors can be due to incorrect programming, unavailable resources, or problems with your computer. In the previous example, a StringIndexOutOfBoundsException occurred due to a programming error in not anticipating a name with no whitespace. The program tried to create a substring starting at index -1 which is out of bounds.

The program can be rewritten to detect the error, and print the name anyway. Note that the program runs to completion. 

 

Listing 2 - UpperCaseHandlesException.java

 1: public class UpperCaseHandlesException 
 2: {
 3:     public static void main(String[] args)
 4:     {
 5:         String[] names = {"Susan Porter", "Javier Jones",
 6:                           "Mickey"};
 7:         for (String name : names)
 8:         {
 9:             try {
10:                 String lastName = name.substring(
11:                                       name.indexOf(' '));
12:                 if (lastName.charAt(1) >= 'M') 
13:                     System.out.println(name);
14:                 else 
15:                     System.out.println(name.toUpperCase());
16:             }
17:             catch (StringIndexOutOfBoundsException ex) {
18:                 System.out.println(name);
19:             }
20:         }
21:     }    
22: }

OUTPUT:

Susan Porter

JAVIER JONES

Mickey

Section 2 - try/catch Statements

The program in the previous example contained a try/catch statement. The try/catch statement identifies statements which may cause an exception and provides other statements to execute when and if the exception occurs. The syntax is as follows:

try {
       statements_which_may_cause_an_exception;
}
catch (Exception ex) {
       statement_executed_when_exception_occurs
}

The keyword try is followed by statements enclosed in curly braces. This is often called a try block. Statements which may cause an exception are placed in the try block, which is  immediately followed by the keyword catch, an exception type and variable name enclosed in parentheses, and statements enclosed in curly braces. This is often called a catch clause. Statements which should be executed when and if the exception occurs are placed in the catch clause.

In the previous example, the statement which causes the exception (the substring call),  and all statements which process its result are placed in the try block. The catch clause processes the StringIndexOutOfBoundsException, and prints the offending name.

 

Section 3 - Execution Flow when Exceptions Occur

When an exception occurs in a try block, the JVM transfers control to the first catch clause that can handle the exception. The remaining statements in the try block do not execute. In the program in Listing 2, the statement on line 10 causes the exception. The JVM  generates a StringIndexOutOfBoundsException which can be caught by the catch clause on lines 17-20. Therefore, the JVM transfers control from line 10 to 17, and lines 12-15 do not execute. Line 18 executes after the exception is caught.

catch clause can only handle the exception defined in its argument or a subclass of this exception. If the exception generated in a try block cannot be caught by the catch clause, the JVM will transfer control to the first catch clause on the execution stack which can handle the exception. If a catching catch clause is not found, the JVM terminates the program.

In the following program, an ArrayIndexOutOfBoundsException is generated inside the try block of the processNames method. This cannot be handled by the catch(StringIndexOutOfBoundsException) clause, so the JVM looks for a suitable catch clause in the execution stack. Since no catching clause exists, the JVM terminates the program. 

 

Listing 3 - UpperCaseArrayIncorrect.java

public class UpperCaseArrayIncorrect 
{
    public static void processNames(String[] names)
    {
        try {
            String lastName = names[3].substring(
                                       names[3].indexOf(' '));
            if (lastName.charAt(1) >= 'M') 
                System.out.println(names[3]);
            else 
                System.out.println(names[3].toUpperCase());
        }
        catch (StringIndexOutOfBoundsException ex) {
            System.out.println(names[3]);
        }
    }
    public static void main(String[] args)
    {
        String[] names = {"Susan Porter", "Javier Jones", "Mickey"};
        processNames(names);
    }    
}

OUTPUT:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3

at UpperCaseArrayIncorrect.processNames(UpperCaseArrayIncorrect.java:7)

at UpperCaseArrayIncorrect.main(UpperCaseArrayIncorrect.java:20)

 

In the following program, method processNames is executed in a try block whose catch clause catches an ArrayIndexOutOfBoundsException. As in the program in Listing 3, an ArrayIndexOutOfBoundsException is generated inside the try block of the processNames method and the exception cannot be handled by the catch(StringIndexOutOfBoundsException) clause. As the JVM searches the execution stack for a suitable catch clause, it transfers control to catch(ArrayIndexOutOfBoundsException) in the main method.

 

Listing 4 - UpperCaseArrayException.java

public class UpperCaseArrayException
{
    public static void processNames(String[] names)
    {
        try {
            String lastName = names[3].substring(
                                       names[3].indexOf(' '));
            if (lastName.charAt(1) >= 'M') 
                System.out.println(names[3]);
            else 
                System.out.println(names[3].toUpperCase());
        }
        catch (StringIndexOutOfBoundsException ex) {
            System.out.println(names[3]);
        }
    }

    public static void main(String[] args)
    {
        String[] names = {"Susan Porter", "Javier Jones", "Mickey"};
        try {
            processNames(names);
        }
        catch (ArrayIndexOutOfBoundsException ex) {
            System.out.println("ArrayIndexOutOfBoundsException");
        }
    }    
}

 

OUTPUT

ArrayIndexOutOfBoundsException

 

Section 4 - Polymorphism and Exceptions

 

In Java, an exception is an object of the class Exception or one of its subclasses. When an exception occurs in your program, the JVM creates an Exception object (or an object of one of its subclasses) and transfers control to the first catch class in the execution stack that can handle the exception. Polymorphism applies to catch clauses, and therefore, if the exception thrown matches the exception in the catch clause or is a subclass or the exception,the JVM will transfer control to the catch clause. If the exception thrown is unrelated to the exception in the catch clause, the JVM will look further up the execution stack for a clause that can handle the exception.  

The Exception class defines several useful methods.The getMessage method returns a string containing information about this exception. This method is often overridden by subclasses. The printStackTrace prints a line for each entry in the execution stack from where the exception was generated to the current location.

 

Section 5 - Multiple Catch Clauses

 

A try/catch statement may contain multiple catch clauses. Since a superclass exception can catch one of its subclasses, exception types should be listed from most specific to least specific. Unrelated exceptions may be listed in any order. The syntax is as follows:

try {
     statements;
}
catch (ExceptionType1 ex) {
     statements;
catch (ExceptionType2 ex) {
     statements;
…

The following program has a try block followed by three catch clauses. StringIndexOutOfBoundsException is a subclass of IndexOutOfBoundsException and must be listed before. IndexOutOfBoundsException is a subclass of Exception and must be listed before. 

 

Listing 5 - MultipleCatchClauses.java

import java.util.Scanner;
import java.util.InputMismatchException;
public class MultipleCatchClauses 
{
    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in); 
        String[] names = {"Susan Porter", "Javier Jones", "Mickey"};
        try {
            System.out.print("Enter name index:");
            int index = scanner.nextInt();
            names[index].substring(names[index].indexOf(' '));
        }  
        catch(StringIndexOutOfBoundsException ex) {
            System.out.println(
                "catch(StringIndexOutOfBoundsException): " +
                 ex.getMessage());
        }
        catch(IndexOutOfBoundsException ex) {
            System.out.println(
                "catch(ArrayIndexOutOfBoundsException): " +
                 ex.getMessage());
        }
        catch(Exception ex) {
            System.out.println("catch(Exception):");
            ex.printStackTrace();
        }
    }
}

If 2 is entered for the index, a StringIndexOutOfBoundsException is generated. This exception is caught by the catch(StringIndexOutOfBoundsException) clause. The getMessage method is overridden by StringIndexOutOfBoundsException to print the offending string index.

 

OUTPUT 1 (Input shown in RED)

Enter name index:2

catch(StringIndexOutOfBoundsException): String index out of range: -1

 

If 3 is entered for the index, an ArrayIndexOutOfBoundsException is generated. This exception is caught by the catch(IndexOutOfBoundsException) clause, since ArrayIndexOutOfBoundsException  is a subclass of IndexOutOfBoundsException . The getMessage method is overridden by ArrayIndexOutOfBoundsException to print the offending array index.

 

OUTPUT 2 (Input shown in RED)

Enter name index:3

catch(ArrayIndexOutOfBoundsException): 3

 

If A is entered for the index, an InputMismatchException is generated. This exception is not related to StringIndexOutOfBoundsException or IndexOutOfBoundsException,  so it is caught by the catch(Exception) clause, since all exceptions are subclasses of the Exception class. The printStackTrace method displays the execution stack from where the exception was generated to the current location.

 

OUTPUT 3 (Input shown in RED)

Enter name index:A

catch(Exception):

java.util.InputMismatchException

at java.util.Scanner.throwFor(Scanner.java:864)

at java.util.Scanner.next(Scanner.java:1485)

at java.util.Scanner.nextInt(Scanner.java:2117)

at java.util.Scanner.nextInt(Scanner.java:2076)

at exceptions.MultipleCatchClauses.main(MultipleCatchClauses.java:11)

 

Note that if the exception clauses were listed in the reverse order (from general to specific), compilation errors would occur. In the following code, the IndexOutOfBoundsException and StringIndexOutOfBoundsException have already been caught by the catch(Exception) clause.

 

catch(Exception ex) {}

catch(IndexOutOfBoundsException ex) {}         // ERROR

catch(StringIndexOutOfBoundsException ex) {}   // ERROR

 

Section 6 - Checked vs. Unchecked Exceptions

 

The exceptions demonstrated so far (StringIndexOutOfBoundsException,  ArrayIndexOutOfBoundsException and InputMismatchException ) are unchecked exceptions. These exceptions typically indicate errors in how a program is run or problems in the user’s environment. It is not mandatory that your program handles unchecked exceptions. All subclasses of the RuntimeException class are unchecked exceptions.

 

Checked exceptions typically involve programming errors, and your programs must catch or throw any checked exceptions that could be generated. All exception classes that do not inherit from RuntimeException are checked exceptions. Consider the following method:

    public static void openFile(String fileName) // ERROR 
    {
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
    }

The FileReader constructor will throw a FileNotFoundException if the file corresponding to fileName does not exist. Since this is a checked exception, you are required to handle it, and a compilation error occurs.

 

The openFile method could catch the exception or a throws clause could be added to the method header indicating that the method could throw a FileNotFoundException

    public static void openFile(String fileName) throws FileNotFoundException // OK
    {
        BufferedReader reader = new BufferedReader(new FileReader(fileName));
    }

An exception object can be explicitly thrown using the throw keyword. The new operator must follow throw in order to create an exception object. The following is another version of the openFile method that explicitly throws a FileNotFoundException if fileName does not exist.

    public static void openFile(String fileName) throws FileNotFoundException
    {
        File file = new File(fileName);
        if(!file.exists())
            throw new FileNotFoundException(fileName);
    }

 

Section 7 - Writing Your Own Exception Classes

 

Suppose you wrote a Person class that contains a name and a social security number. 

 

Listing 6 - Person.java (version 1)

public class Person 
{
    private String name;
    private String ssn;

    public Person(String n, String s)
    {
        name = n;
        ssn  = s;
    }

    public String getName() { return name; }
    public String getSsn()  { return ssn;  }
}

The program in Listing 7 creates a Person object in a loop, and then processes the person by printing out his or her name followed by social security number as an integer. Is the string entered for social security number does not represent an integer, the program throws a NumberFormatException, then terminates.

 

Listing 7 - PersonClient.java (version 1) 

import java.util.Scanner;
public class PersonClient
{
    public static void processPerson(Person person) 
    { 
        System.out.print(person.getName());
        System.out.println(" " + Integer.parseInt(person.getSsn())); 
    }

    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
        boolean done = false;
        while (!done)
        {
            boolean valid = true;
            System.out.print("Enter the person's name:");
            String name = new String(scanner.nextLine());
            System.out.print("Enter the person's SSN:");

            String ssn = new String(scanner.nextLine());
            Person person = new Person(name, ssn);
            processPerson(person);

            System.out.print("Enter 'Y' to continue:");
            done = scanner.nextLine().charAt(0) != 'Y';
        }
    }
}

PROGRAM OUTPUT (Input shown in RED):

Enter the person's name:Nancy

Enter the person's SSN:1234567A9

Exception in thread "main" java.lang.NumberFormatException: For input string: "1234567A9" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)

at java.lang.Integer.parseInt(Integer.java:580)

at java.lang.Integer.parseInt(Integer.java:615)

at exceptions.PersonClient2.processPerson(PersonClient.java:7)

at exceptions.PersonClient2.main(PersonClient.java:21)

 

To prevent the exception from occurring, the client could be modified to avoid creation of the Person object if the string used for ssn does not contain digits. It is unreasonable, however, to make clients responsible for the validation of the classes they utilize (consider the consequences if the classes in the Java API were designed in this manner!). 

If the social security number is tested for digits inside the Person class constructor, the client would no longer be responsible for validating the class it utilizes. This approach is still undesirable, since a Person object that is unusable is constructed and made available to the client. The constructor has no choice but to call System.exit and terminate the program.

 

Listing 8 - Person.java (version 2)

public class Person 
{
    private String name;
    private String ssn;

    public Person(String n, String s)
    {
        name = n;
        ssn  = s;

        for (int i = 0; i < ssn.length(); ++i)
            if (!Character.isDigit(ssn.charAt(i)))
            {
                System.out.println("Invalid Person");
                System.exit(1);
            }
    }

    public String getName() { return name; }
    public String getSsn()  { return ssn;  }
}

A better approach is to throw an exception inside the Person class constructor if the object about to be created is invalid. Since the client catches (or propagates) the exception, and the Person object is created and processed in a try block, there is no chance of using an invalid object.

The Person class constructor could throw an IllegalArgumentException, but let’s design a custom exception that contains information pertaining to the Person class. Custom exception classes can be created by inheriting the Exception class. These classes may contain fields, and typically override the getMessage method.

The PersonException class in Listing 9 contains fields for the offending character and its position in the string. The getMessage method is overridden to print a suitable error message.

Listing 9 - PersonException.java

public class PersonException extends Exception 
{
    private int position;
    private char character;

    public PersonException(int p, char c)
    {
        position = p;
        character = c;
    }

    public String getMessage()
    {
        return "PersonException: " + character 
             + " found at position " + position;
    }
}

The Person class is then rewritten to throw a PersonException if the ssn contains digits. 

 

Listing 10 - Person.java (version 3)

public class Person 
{
    private String name;
    private String ssn;

    public Person(String n, String s) throws PersonException
    {
        name = n;
        ssn  = s;

        for (int i = 0; i < ssn.length(); ++i)
            if (!Character.isDigit(ssn.charAt(i)))
                throw new PersonException(i, ssn.charAt(i));
    }

    public String getName() { return name; }
    public String getSsn()  { return ssn;  }
}

The client is then rewritten to create and process the Person object inside a try block whose 

corresponding catch clause catches a PersonException. The Person class constructor propagates the exception to the client, which calls its getMessage method, then the user is prompted to enter the name and ssn of another person.

 

Listing 11 - PersonClient.java (version 2)

import java.util.Scanner;
public class PersonClient 
{
    public static void processPerson(Person person) 
    { 
        System.out.print(person.getName());
        System.out.println(" " + Integer.parseInt(person.getSsn())); 
    }

    public static void main(String[] args)
    {
        Scanner scanner = new Scanner(System.in);
        boolean done = false;
        while (!done)
        {
            System.out.print("Enter the person's name:");
            String name = new String(scanner.nextLine());
            System.out.print("Enter the person's SSN:");
            String ssn = new String(scanner.nextLine());

            try {
                Person person = new Person(name, ssn);
                processPerson(person);
            }
            catch (PersonException ex) {
                System.out.println("PersonException:" 
                                 + ex.getMessage());
            }

            System.out.print("Enter 'Y' to continue:");
            done = scanner.nextLine().charAt(0) != 'Y';
        }
    }
}

PROGRAM OUTPUT (Input shown in RED):

 

Enter the person's name:Nancy

Enter the person's SSN:1234567A9

PersonException:PersonException: A found at position 7

Enter 'Y' to continue:Y

Enter the person's name:Nancy

Enter the person's SSN:123456789

Nancy 123456789

Enter 'Y' to continue:

 

This tutorial was written by:

CLICK HERE

to read books by Ralph