Color ClassColor class whose constructor accepts r, g, b values in the range of 0 … 255. Any value
outside of that range produces an invalid color and attempting any operation results in either some exception or
a meaningless result. Further assume a Color object if the read is successful, or a
Write an app that reads Color objects from a file, prints them out (using the class' toString
method, and invokes the isGrey, and lighter methods. OTOH, if the r, g, b value(s) were invalid, an appropriate
error message should be printed, and none of the subsequent operations should be performed, and execution should proceed to the next set of values to be read in.
public Color(int r, int g, int b) {
if (r < 0 || r 255 || g < 0 || …) ??????? // WHat do we do here?
this.r = r;
this.g = g;
this.b = b;
}
boolean return type and
returning read Method
public static Color read(
if (!scanner.hasNextInt()) return null;
int r = scanner.nextInt();
int g = scanner.nextInt();
int b = scanner.nextInt();
return new Color(r, g, b); // may 'blow up' in constructor
}
null is
reserved for no more data
Color c = Color.read(scanner); while (???) { if (???) { System.out.println(r); System.out.println(r.isGrey()); System.out.println(r.lighter()); } c = Color.read(scanner); }
null to signal the end of data in our read methods
… double d = Math.sqrt(d2); if ( the value ofdis the special sentinel value) { handle the situation of having sent a negative argument tosqrt… } else { 'normal' situation … }
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 known as a sentinel 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.
nullnull is really just a special case of a sentinel value. As null is a legal value for all object types, returning null is a way to indicate
that something went wrong (or at least is different, or not 'normal').
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 a secondary ireason for presenting this approach::
first, to show that null can act as a sentinel 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, and vice versa; 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, we'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 firstName = 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 a sentinel (-1)
public static int validateAssignment(int grade) {
return grade >= 0 && grade <= 10 ? grade : -1;
}
// Uses a 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 a 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()); // using null
if (lastName == null) {
error(dataRecord, "Invalid last name");
continue;
}
String firstName = DataValidation.validateName(lineScanner.next()); // using null
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()); // using -1 (sentinel)
if (assignment == -1) {
error(dataRecord, "Invalid assignment grade");
isInvalid = true;
}
}
if (isInvalid) continue;
String project = lineScanner.next();
DataValidation.Result result = DataValidation.validateProject(project); // using result object
if (!result.isValid) {
error(dataRecord, "Invalid project grade");
continue;
}
int midterm = lineScanner.nextInt();
if (!DataValidation.validateExam(midterm)) { // using guard method
error(dataRecord, "Invalid midterm grade");
continue;
}
int finalExam = lineScanner.nextInt();
if (!DataValidation.validateExam(finalExam)) { // using guard method
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);
}
}
hasNextxxx of Scanner
Student class containing an array of grades:
class Student {
…
int [] getGrades() {return grades;}
…
private int [] grades;
}
and here is an app:
class PrintDeansListApp {
public static void main(String [] args) {
…:
Student student = Student.read(scanner);
while (student != null) {
System.out.println(student + " is on dean's list: " + isOnDeansList(student.getGrades()));
student = Student.read(scanner);
}
}
public static boolean isOnDeansList(int [] grades) {
double avg = average(grades);
return avg >= 90;
}
public static double average(int [] a) {
if (a.length == 0) ????;
int sum = 0;
for (int x : a)
sum += x;
return (double) sum / a.length;
}
}
isOnDeansList)?
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.
catchblock, 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
throws Clause… void f() throws FileNotFoundException { // no try/catch .. exception will be propgated up the call chain … g(); … } void g() throws FileNotFoundException { … Scanner sc = new Scanner(new File("somefile.text")); … }
try/catch blocks can be nested
catch sequence or it will handle the narrower one.
// 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());
}
}
}
}