Chapter 14: Exception Handling Text I/O

Writing functions

Functions should not do input and output. They should not use global or module-level variables. They should communicate with the caller in two ways: (1) get values from the caller through the argument list, and (2) return a value to the caller with a return statement. If the function is not void, use its return value!

Run time error: A run-time error occurs when a program is running and it is given an instruction that is impossible for it to carry out. Some obvious examples are

(1)Dividing by 0 (although Java allows division by 0 when using floats or doubles, but not integers).

(2)Trying to open a file that doesn't exist or read past the end of an existing file.

(3)Trying to convert a non-numeric string to a numeric value.

(4)Trying to access an element of an array that doesn't exist (because the array index is either less than 0 or it is greater than or equal to the number of elements in the array).

14.2 Exception-Handling Overview

Consider the following program (Listing 14.1):

Listing 14.1

import java.util.Scanner;

public class Quotient {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);

// Prompt the user to enter two integers

System.out.print("Enter two integers: ");

int number1 = input.nextInt();

int number2 = input.nextInt();

System.out.println(number1 + " / " + number2 + " is " +(number1 / number2));

}

}

Try to run it with the second integer equal to 0. The program blows up.

There are two solutions to the problem. One solution is to protect the offending statement by putting it in an if statement (Listing 14.2).

Listing 14.2

import java.util.Scanner;

public class QuotientWithIf {

public static void main(String[] args) {

Scanner input = new Scanner(System.in);

// Prompt the user to enter two integers

System.out.print("Enter two integers: ");

int number1 = input.nextInt();

int number2 = input.nextInt();

if (number2 != 0)

System.out.println(number1 + " / " + number2 + " is " +

(number1 / number2));

else

System.out.println("Divisor cannot be zero ");

}

}

Yet another way is to exit the program when an error occurs (Listing 14.3). This is a bad way to handle the problem because it allows the method to decide to terminate the program, a decision that probably should be left to the main program:

Listing 14.3

import java.util.Scanner;

public class QuotientWithMethod

{

public static int quotient(int number1, int number2)

{

if (number2 == 0)

{ System.out.println("Divisor cannot be zero");

System.exit(1);

}

return number1 / number2;

}

public static void main(String[] args)

{

Scanner input = new Scanner(System.in);

// Prompt the user to enter two integers

System.out.print("Enter two integers: ");

int number1 = input.nextInt();

int number2 = input.nextInt();

int result = quotient(number1, number2);

System.out.println(number1 + " / " + number2 + " is "

+ result);

}

System.out.println("Execution continues ...");

}

}

A better way is to do something called "throwing" an exception, and allowing the main program to decide what to do about it.

Below is Listing 14.4. The programmer throws his own exception. However, most of the time when something illegal happens in a program, the system creates its own exception.

Listing 14.4

import java.util.Scanner;

public class QuotientWithException {

public static int quotient(int number1, int number2) {

if (number2 == 0)

throw new ArithmeticException("Divisor cannot be zero");

return number1 / number2;

}

public static void main(String[] args) {

Scanner input = new Scanner(System.in);

// Prompt the user to enter two integers

System.out.print("Enter two integers: ");

int number1 = input.nextInt();

int number2 = input.nextInt();

try {

int result = quotient(number1, number2);

System.out.println(number1 + " / " + number2 + " is "

+ result);

}

catch (ArithmeticException ex) {

System.out.println("Exception: an integer " +

"cannot be divided by zero ");

}

System.out.println("Execution continues ...");

}

}

14.3 Exception Handling Advantages

Exceptions allow methods to avoid dealing with an error by passing it "up" to the caller (which can also pass it "up" to its caller, etc.). If it didn't work this way, then the exception would have to be handled where it occurs. The problem with this is that the method where the exception occurs may not know what to do to handle it!

14.6 The finally Clause

If there is some code that you want to be executed whether an exception occurs or not, you can add a finally clause.

try

{

statements;

}

catch (Exception e)

{

handle the exception;

}

finally

{

final statements;

}

Common use: to make sure that a file gets closed, regardless of whether an error occurred or not.

14.7 When to Use Exceptions

Only throw exceptions when you cannot handle the problem in the method where the exception occurred.

Not this:

try

{

System.out.println (refVar.toString());

}

catch(NullPointerException ex)

{

System.out.println ("refVar is null");

}

Use this instead:

if (refVar != null)

System.out.println (refVar.toString());

else

System.out.println ("refVar is null");

Text Files: Using a Scanner object for input

Reading an input file with multiple data items on each line.

Let's create a program that will take text that has "tokens" on it (separated by white space):

  • First name
  • Middle initial
  • Last name
  • Score

And split the line into a name and a score. The easiest way to do this is to use the Scanner object, which splits a string into "tokens" delimited by white space (spaces, tabs, carriage returns).

Code to read text strings and ints into an array of strings and ints

Note that this can be done with a file using the same code except for the line that creates the new Scanner.

public static void readStringsAndIntsFile() throws Exception

{

Scanner input = new Scanner("JohnTSmith90Eric KJones85");

String[] firstName = new String[10];

String[] middleInitial = new String[10];

String[] lastName = new String[10];

int[] score = new int [10];

int i = 0;

while (input.hasNext())

{ firstName[i] = input.next();

middleInitial[i] = input.next();

lastName[i] = input.next();

score[i] = input.nextInt();

i++;

}

int lineCount = i;

// Now print the array

System.out.println ("\nNames and Scores");

for (i=0; i<lineCount; i++)

System.out.println (String.format("%15s %1s %15s %5d",

firstName[i], middleInitial[i], lastName[i], score[i]));

}

You can define your own delimiter character(s). Make the following changes:

Scanner input = new Scanner("John/T/Smith/90/Eric/K/Jones/85");

// Put delimiter characters in square brackets

input.useDelimiter("[/]");

You can check for multiple delimiter characters:

Scanner input = new Scanner("John T/Smith^90Eric/K^Jones/85");

// Put delimiter characters in square brackets: space, /, ]

input.useDelimiter("[/^]");

You can also use control characters (like tab, carriage return):

Scanner input = new Scanner("John T/Smith^90\nEric\tK\rJones/85");

// Put delimiter characters in square brackets: space, /, ^, \n, \t, \r

input.useDelimiter("[/^\n\t\r]");

Note that the above looks for exactly ONE of the delimiter characters. If there are two, then the scanner assumes that there are two tokens! So if you have two blanks separating two tokens, you MAY get an error. I have added an extra space before the word "Eric":

Scanner input = new Scanner("John T/Smith^90 Eric/K^Jones/85");

// Put delimiter characters in square brackets

input.useDelimiter("[/^]");

This (above) generates an error only because we are now trying to read the word "Jones" into an array of integers!

If we want to tell the scanner to treat multiple delimiters as a single delimiter (frequently there may be many spaces between tokens), we need to put a plus sign after the list of delimiters, like this:

Scanner input = new Scanner(

"John^T^Smith^^^^^^90 Eric\t \n\n\r K^Jones//////85");

// Put delimiter characters in square brackets

input.useDelimiter("[ /^\t\n\r]+");

The "+" means "one or more of the preceding".

Special characters in the argument to useDelimiter:

  • [] anything in square brackets means "pick one"
  • \t = tab
  • \n = new line
  • \r = carriage return
  • + = one or more of the preceding

Text Files: Using a PrintWriter for output

The textbook recommends using a File object and a PrintWriter object to write to a text file.

Listing 14.13

public class WriteData {

public static void main(String[] args) throws Exception {

java.io.File file = new java.io.File("scores.txt");

if (file.exists()) {

System.out.println("File already exists");

System.exit(0);

}

// Create a file

java.io.PrintWriter output = new java.io.PrintWriter(file);

// Write formatted output to the file

output.print("John T Smith ");

output.println(90);

output.print("Eric K Jones ");

output.println(85);

// Close the file

output.close();

}

}

Text files: input

Text files can be read using a Scanner object combined with a file object. Note that the following causes two errors:

public static void main(String[] args)

{

File inFile = new File("scores.txt");

Scanner input = new Scanner(inFile);

while (input.hasNext())

{

int n = input.nextInt();

System.out.println(n);

}

}

Error 1: We need to import the File class.

Error 2: We need to add throws Exception to the end of the first line.

If we fix both of these things, it should work.

However, instead of addint throws Exception to the end of the main declaration, we can catch the exception (so it does NOT get thrown).

try

{

File inFile = new File("scores.txt");

Scanner input = new Scanner(inFile);

}

catch (Exception ex)

{

System.out.println("No file found!");

System.exit(0);

}

This also causes a problem: We can't refer to infile or input outside of the block they are declared in. So we have to separate their declaration and initialization:

File inFile

Scanner input

try

{

inFile = new File("scores.txt");

input = new Scanner(inFile);

}

catch (Exception ex)

{

System.out.println("No file found!");

System.exit(0);

}

This causes yet another problem! We get a compile error in the while loop telling us that our Scanner object might not be initialized. The ony way we can fix this is to move the while loop into the try block.

File inFile;

Scanner input = new Scanner(System.in);

try

{

inFile = new File("scores.txt");

input = new Scanner(inFile);

while (input.hasNext())

{

int n = input.nextInt();

System.out.println(n);

}

} catch (Exception ex)

{

System.out.println("No file found!");

System.exit(0);

}

Chapter14--ExceptionsAndTextIO.docx111/17/2018