main
, but the file not actually opened
until sometime later, within a called method.
main
where the user is prompted for the file name
double average(int [] arr) { int total = 0; for (int i = 0; i < arr.length; i++) total += i; return (double)total / arr.length; }
// Uses a (special) trailer value import java.io.*; import java.util.*; public class Averager { public static void main(String [] args) throws Exception { int [] arr1 = {1, 2, 3, 4}, arr2 = {}; doIt(arr1); System.out.println(); doIt(arr2); } static void doIt(int [] arr) { System.out.println("The array: " + toString(arr)); double d = average(arr); if (Double.isNaN(d)) System.out.println("0-length array ... average couldn't be taken"); else System.out.println("average: " + d); } static double average(int [] arr) { if (arr.length == 0) return Double.NaN; double total = 0; for (int i = 0; i < arr.length; i++) total += arr[i]; return (double)total/arr.length; } static String toString(int [] arr) { String result = "{"; for (int i = 0; i < arr.length; i++) result += arr[i] + (i < arr.length-1 ? ", " : ""); return result + "}"; } }
double
actually has Double.NaN
— an acceptable trailer value for all situations, i.e., a value that represents the absence of a
valid double
.
Double.NaN
is a static
variable of type double
from the Double
(wrapper) class in java.lang
.
average
method checks for a 0-length array and returns this trailer value in that situation
double
is returned.
// Uses a 'result' object containing status of operation and value import java.io.*; import java.util.*; public class Averager { static class Result { Result(double average, boolean wasSuccessful) { this.average = average; this.wasSuccessful = wasSuccessful; } Result(boolean b) {this(0, false);} Result(double average) {this(average, true;} public double average; public boolean wasSuccessful; } public static void main(String [] args) throws Exception { int [] arr1 = {1, 2, 3, 4}, arr2 = {}; doIt(arr1); System.out.println(); doIt(arr2); } static void doIt(int [] arr) { System.out.println("The array: " + toString(arr)); Result result = average(arr); if (!result.wasSuccessful) System.out.println("0-length array ... average couldn't be taken"); else System.out.println("average: " + result.average); } static Result average(int [] arr) { if (arr.length == 0) return new Result(false); double total = 0; for (int i = 0; i < arr.length; i++) total += arr[i]; return new Result((double)total / arr.length); } static String toString(int [] arr) { String result = "{"; for (int i = 0; i < arr.length; i++) result += arr[i] + (i < arr.length-1 ? ", " : ""); return result + "}"; } }
boolean
status and a double
for the average.
Pair
class is often provided by the language's
predefined class library.
boolean
and double
values.
Result
class … as mentioned above, this class is temporary class in that it is briefly used for the purpose of passing multiple return values back from a called method.
Averager
class, and thus, rather than cluttering our class space with another class name, we make it an
nested class, i.e., a class defined within another class definition.
static
in the class header,
and is called a static nested class
Result
is a class used specificaly and only by Averager
.
private
), the class is referred to as OuterClass.NestedClass,
e.g., Averager.Result.
Result
.
Result
class are declared public
. Given the temporary and short-lived use of the object of this class (their use is
basically limited to returning the values from the method), there is no real reason to 'make them bulletproof' or provide any real semantics via methods; all
that is really needed is for the caller to be able to access the instance variables.
Result
object. In the case of a result object, we typically have the usual workhorse
constructor (that accepts all values and initializes them), and then one for the valid case and one for the invalid case.
// Uses a 'guard' (pre-condition) method -- similar to the hasXXXX methods of Scanner import java.io.*; import java.util.*; public class Averager { public static void main(String [] args) throws Exception { int [] arr1 = {1, 2, 3, 4}, arr2 = {}; doIt(arr1); System.out.println(); doIt(arr2); } static void doIt(int [] arr) { System.out.println("The array: " + toString(arr)); if (!hasAverage(arr)) System.out.println("0-length array ... average couldn't be taken"); else System.out.println("average: " + average(arr)); } static boolean hasAverage(int [] arr) {return arr.length != 0;} static double average(int [] arr) { // if !hasAverage(arr) ... double total = 0; for (int i = 0; i < arr.length; i++) total += arr[i]; return (double)total / arr.length; } static String toString(int [] arr) { String result = "{"; for (int i = 0; i < arr.length; i++) result += arr[i] + (i < arr.length-1 ? ", " : ""); return result + "}"; } }
Color
of the Labs; the isValid
method was used to check the validity of the
Color
object's instance variables before executing other methods which could fail if those variables were out of range.
null
null
is really just a special trailer value case. As null
is a legal value for all object types, returning null is a way to indicate
that something went wrong.
null
is a legitimate return value for the code (and thus ineligible as a true trailer value); we'll ignore that
situation.
double
(had it been a String
for example — which is an object
— it would be more straightforward. We wouldn't expect to be able to use null
for double
and that is the secondary purpose of this approach:
first, to show that null
can act as a trailer value and secondly, that there is a way to bring the primitives into the world of objects, namely using the
primitive type wrapper classes. We will have a lot more to say about them when we talk about inheritance, the class hierarchy and collections later on.
The main takeaway from this example should not be the Double
/double
sleight-of-hand; that will be presented later. Rather, it's the use
of null
as the trailer value of sorts, and that this is thus applicable to all situations using objects as the return value.
/// Uses null as the exceptional case indicator import java.io.*; import java.util.*; public class Averager { public static void main(String [] args) throws Exception { int [] arr1 = {1, 2, 3, 4}, arr2 = {}; doIt(arr1); System.out.println(); doIt(arr2); } static void doIt(int [] arr) { System.out.println("The array: " + toString(arr)); Double d = average(arr); if (d == null) System.out.println("0-length array ... average couldn't be taken"); else System.out.println("average: " + d); } static Double average(int [] arr) { if (arr.length == 0) return null; double total = 0; for (int i = 0; i < arr.length; i++) total += arr[i]; return (double)total/arr.length; } static String toString(int [] arr) { String result = "{"; for (int i = 0; i < arr.length; i++) result += arr[i] + (i < arr.length-1 ? ", " : ""); return result + "}"; } }
java.lang
: Integer
, Double
,
Boolean
, Character
, etc.
null
(which only works for objects), we use Double
instead of double
double
the Double
object is converted; again, this will be explained later.
Scanner keyboard = new Scanner(System.in);
System.out.print("File name? ");
String fname = keyboard.next();
Scanner scanner = new Scanner(new File(fname));
filePrint(String filename)
?
import java.io.*; import java.util.*; public class FileOpener { public static void main(String [] args) throws Exception { Scanner keyboard = new Scanner(System.in); Scanner datafile = null; String filename; while (true) { System.out.print("filename? "); filename = keyboard.next(); File file = new File(filename); if (file.exists()) { datafile = new Scanner(file); break; } else System.out.println("'" + filename + "' not found, try again!"); } // ... and here we go System.out.println("=== " + filename); System.out.println("-----------------"); while (datafile.hasNextLine()) { String line = datafile.nextLine(); System.out.println(line); } } }
exists
method of the File
class to check that we will
be able to open the file
break
; once we break out of the loop, wer're good too go
import java.io.*; import java.util.*; public class FileOpener { public static void main(String [] args) throws Exception { Scanner keyboard = new Scanner(System.in); while (true) { System.out.print("filename? "); String filename = keyboard.next(); if (filePrint(filename)) break; System.out.println("'" + filename + "' not found, try again!"); } } static boolean filePrint(String filename) throws Exception { File file = new File(filename); if (!file.exists()) return false; Scanner scanner = new Scanner(file); while (scanner.hasNextLine()) { String line = scanner.nextLine(); System.out.println(line); } return true; } }
class DataIntegrityChecker { public static void main(String [] args) { Scanner fileScanner = new Scanner(new File("data.text")); while (fileScanner.hasNext()) { String lastName = scanner.next(); String lastName = scanner.next(); int numAssignments = scanner.nextInt(); for (int i = 1; i <= numAssignments; i++) { int assignment = scanner.nextInt(); assignmentTotal += assignment; } String project = scanner.next(); int midterm = scanner.nextInt(); int finalExam = scanner.nextInt(); } }
class DataValidation { // Uses null public static String validateName(String name) { if (name.length() == 0) return null; if (!Character.isUpperCase(name.charAt(0))) return null; for (int i = 1; i < name.length(); i++) if (!Character.isLowerCase(name.charAt(i))) return null; return name; } // Uses trailer value (-1) public static int validateAssignment(int grade) { return grade >= 0 && grade <= 10 ? grade : -1; } // Uses result object static class Result { Result(boolean isValid, String grade) { this.isValid = isValid; this.grade = grade; } Result(String grade) {this(true, grade);} Result(boolean isValid) {this(isValid, "");} public boolean isValid; public String grade; } public static Result validateProject(String grade) { return "ABCDF".indexOf(grade) >= 0 ? new Result(grade) : new Result(false); } // Uses guard method public static boolean validateExam(int grade) { return grade >= 0 && grade <= 100 ? true : false; } }
import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException; class DataIntegrityChecker { public static void main(String [] args) throws FileNotFoundException { Scanner fileScanner = new Scanner(new File("../data.text")); int dataRecord = 0; while (fileScanner.hasNextLine()) { String line = fileScanner.nextLine().trim(); if (line.length() == 0) continue; dataRecord++; Scanner lineScanner = new Scanner(line); String lastName = DataValidation.validateName(lineScanner.next()); if (lastName == null) { error(dataRecord, "Invalid last name"); continue; } String firstName = DataValidation.validateName(lineScanner.next()); if (firstName == null) { error(dataRecord, "Invalid first name"); continue; } int numAssignments = lineScanner.nextInt(); boolean isInvalid = false; for (int i = 1; i <= numAssignments; i++) { int assignment = DataValidation.validateAssignment(lineScanner.nextInt()); if (assignment == -1) { error(dataRecord, "Invalid assignment grade"); isInvalid = true; } } if (isInvalid) continue; String project = lineScanner.next(); DataValidation.Result result = DataValidation.validateProject(project); if (!result.isValid) { error(dataRecord, "Invalid project grade"); continue; } int midterm = lineScanner.nextInt(); if (!DataValidation.validateExam(midterm)) { error(dataRecord, "Invalid midterm grade"); continue; } int finalExam = lineScanner.nextInt(); if (!DataValidation.validateExam(finalExam)) { error(dataRecord, "Invalid final grade"); continue; } System.out.println("#" + dataRecord + ": " + lastName + " " + firstName + " validated"); } } private static void error(int dataRecord, String message) { System.out.println("*** In data record #" + dataRecord + ": " + message); } }
class DataValidation { // Uses guard methods throughout public static boolean validateName(String name) { if (name.length() == 0) return false; if (!Character.isUpperCase(name.charAt(0))) return false; for (int i = 1; i < name.length(); i++) if (!Character.isLowerCase(name.charAt(i))) return false; return true; } public static boolean validateAssignment(int grade) { return grade >= 0 && grade <= 10 ? true : false; } public static boolean validateProject(String grade) { return "ABCDF".indexOf(grade) >= 0 ? true : false; } // Uses guard method public static boolean validateExam(int grade) { return grade >= 0 && grade <= 100 ? true : false; } }
import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException; class DataIntegrityChecker { public static void main(String [] args) throws FileNotFoundException { Scanner fileScanner = new Scanner(new File("../data.text")); int dataRecord = 0; recordReadLoop: while (fileScanner.hasNextLine()) { String line = fileScanner.nextLine().trim(); if (line.length() == 0) continue; dataRecord++; Scanner lineScanner = new Scanner(line); String lastName = lineScanner.next(); if (!DataValidation.validateName(lastName)) { error(dataRecord, "Invalid last name"); continue; } String firstName = lineScanner.next(); if (!DataValidation.validateName(firstName)) { error(dataRecord, "Invalid first name"); continue; } int numAssignments = lineScanner.nextInt(); boolean isInvalid = false; for (int i = 1; i <= numAssignments; i++) { int assignment = lineScanner.nextInt(); if (!DataValidation.validateAssignment(assignment)) { error(dataRecord, "Invalid assignment grade"); isInvalid = true; } } if (isInvalid) continue; String project = lineScanner.next(); if (!DataValidation.validateProject(project)) { error(dataRecord, "Invalid midterm grade"); continue; } int midterm = lineScanner.nextInt(); if (!DataValidation.validateExam(midterm)) { error(dataRecord, "Invalid midterm grade"); continue; } int finalExam = lineScanner.nextInt(); if (!DataValidation.validateExam(finalExam)) { error(dataRecord, "Invalid final grade"); continue; } System.out.println("#" + dataRecord + ": " + lastName + " " + firstName + " validated"); } } private static void error(int dataRecord, String message) { System.out.println("*** In data record #" + dataRecord + ": " + message); } }
main
attempts to pass the exception upwards in
which case the Java runtime takes over and produces a comprehensive, but heavily technical diagnostic message — useless to anyone
but a Java programmer.
throw
statement.
throw
Statementthrow
statement is the basic mechanism of exception notification. The detecting code for the most part has no
idea who will handle the exception, thus the term throw is quite appropriate — i.e., it has no idea who will
be catching the exception notification.
new Exception
), passing the
constructor a string containing information about the nature of the error/exception (e.g.,
new Exception("capacity exceeded")).
if (size == capacity) throw new Exception("capacity exceeded");
… if (y == 0) throw new Exception("zero divide attempted") z = x / y;
void double calcAverage(int [] arr, int n) { if (n == 0) throw new Exception("can't take average of 0-length array"); … }
try
/catch
Blockmain
try
/catch
block.
try { ... contains code (or a call to a method) that may throw an exception ... } catch (declaration of a variable receiving the thrown expression (a reference variable of some 'exception' class) { ... code handling the exception; the exception variable is in scope here } catch (declaration of a variable receiving the thrown expression (a reference variable of some other 'exception' class) { ... code handling the exception; the exception variable is in scope here } …
try
block, execution flow jumps to the first corresponding catch
block/clause
catch
block
catch
block.
catch
block, execution proceeds as normal with the next statement.
Exception
— subsumes ALL exception classes (will make more sense one we've covered inheritance)
IOException
— some I/O operation caused an error
FileNotFoundException
— self-explanatory; subsumed by IOException
NumberFormatException
— trying to convert a string to a numeric, and the string contains non-numeric characters
NullPointerException
— attempting to deference null (e.g., name.getFirst()
, and name
is null
).
ArrayIndexOutOfBoundsException
— self-explanatory
NoSuchElementException
— attempting to access an element (for the moment of a Scanner) and there is no such element
try/catch
blocks can be nested
// Uses exception handling import java.io.*; import java.util.*; public class Averager { public static void main(String [] args) throws Exception { int [] arr1 = {1, 2, 3, 4}, arr2 = {}; doIt(arr1); System.out.println(); doIt(arr2); } static void doIt(int [] arr) { System.out.println("The array: " + toString(arr)); try { System.out.println("average: " + average(arr)); } catch (Exception e) { System.out.println(e.getMessage()); } } static double average(int [] arr) throws Exception { if (arr.length == 0) throw new Exception("0-length array ... average couldn't be taken"); double total = 0; for (int i = 0; i < arr.length; i++) total += arr[i]; return (double)total / arr.length; } static String toString(int [] arr) { String result = "{"; for (int i = 0; i < arr.length; i++) result += arr[i] + (i < arr.length-1 ? ", " : ""); return result + "}"; } }
// uses exception-handling import java.io.*; import java.util.*; public class FileOpener { public static void main(String [] args) { Scanner keyboard = new Scanner(System.in); while (true) { try { System.out.print("filename? "); String filename = keyboard.next(); filePrint(filename); break; } catch (Exception e) { System.out.println(e.getMessage()); } } } static void filePrint(String filename) throws Exception { File file = new File(filename); if (!file.exists()) throw new Exception("'" + filename + "' not found, try again!"); Scanner scanner = new Scanner(new File(filename)); while (scanner.hasNextLine()) { String line = scanner.nextLine(); System.out.println(line); } } }
class DataValidation { public static String validateName(String name) throws Exception { if (name.length() == 0) throw new Exception("0-length name"); if (!Character.isUpperCase(name.charAt(0))) throw new Exception("Non-uppercase first letter"); for (int i = 1; i < name.length(); i++) if (!Character.isLowerCase(name.charAt(i))) throw new Exception("Non-lowercase internal letter"); return name; } public static int validateAssignment(int grade) throws Exception { if (grade < 0 || grade > 10) throw new Exception("Invalid assignment grade"); return grade; } public static String validateProject(String grade) throws Exception { if ("ABCDF".indexOf(grade) < 0) throw new Exception("Invalid project grade"); return grade; } public static Integer validateExam(int grade) throws Exception { if (grade < 0 || grade > 100) throw new Exception("Invalid exam grade"); return grade; } }
import java.util.Scanner; import java.io.File; import java.io.FileNotFoundException; class DataIntegrityChecker { public static void main(String [] args) throws FileNotFoundException { Scanner fileScanner = new Scanner(new File("../data.text")); int dataRecord = 0; while (fileScanner.hasNext()) { String line = fileScanner.nextLine().trim(); if (line.length() == 0) continue; dataRecord++; Scanner lineScanner = new Scanner(line); try { String lastName = DataValidation.validateName(lineScanner.next()); String firstName = DataValidation.validateName(lineScanner.next()); int numAssignments = lineScanner.nextInt(); for (int i = 1; i <= numAssignments; i++) { int assignment = DataValidation.validateAssignment(lineScanner.nextInt()); } String project = DataValidation.validateProject(lineScanner.next()); int midterm = DataValidation.validateExam(lineScanner.nextInt()); int finalExam = DataValidation.validateExam(lineScanner.nextInt()); System.out.println("#" + dataRecord + ": " + lastName + " " + firstName + " validated"); } catch (Exception e) { System.out.println("*** Exception ... In data record #" + dataRecord + ": " + e.getMessage()); } } } }