Chapter 2 Additional Features of Java Programming

Preview:

Citation preview

Chapter 2

Additional Features of

Java Programming

The static modifier is applied to constant identifiers (always) and method identifiers (sometimes). Static means “applying to the class as a whole, rather than to an instance of the class.”

For example, in the Integer class:

public static final int MAX_VALUE = 0x7fffffff;

= 231 - 1

There is only one copy of

MAX_VALUE instead of a separate copy for each instance of Integer. final means “Can be assigned to only once.”

To access this constant, the class name is followed by the member-designator operator (the dot) followed by the constant identifier: if (size == Integer.MAX_VALUE)

Here’s a method from java.lang.Math: public static double ceil (double a)

returns the smallest value that is greater than or equal to a and is mathematically equal to an integer. System.out.println (Math.ceil (3.17));

The output will be 4.0. There is no calling object; the argument has all that the method needs.

METHOD TESTING WITH JUnit

A method is correct if it satisfies its specification. Your confidence in the correctness of your methods is increased by testing, which can reveal the presence but not absence of errors.

JUnit is a free software package for testing a class’s

methods.

For example,

here is an indirect test of the 3-parameter constructor in the HourlyEmployee class:

@Test

public void test3()

{

hourly = new HourlyEmployee ("terry", 41, 20.00);

assertEquals (800.00, hourly.getRegularPay());

assertEquals (30.00, hourly.getOvertimePay());

assertEquals (830.00, hourly.getGrossPay());

} // method test3

import org.junit.*;import static org.junit.Assert.*;import org.junit.runner.Result;import static org.junit.runner.JUnitCore.runClasses;

import java.util.*;

public class HourlyEmployeeTest{ public static void main(String[ ] args) { Result result = runClasses (HourlyEmployeeTest.class); System.out.println ("Tests run = " + result.getRunCount() + "\nTests failed = " + result.getFailures()); } // method main  

@Test public void test1() { hourly = new HourlyEmployee ("andrew", 39, 10.00); assertEquals (390.00, hourly.getRegularPay()); assertEquals (0.00, hourly.getOvertimePay()); assertEquals (390.00, hourly.getGrossPay()); } // method test1

@Test public void test2() { hourly = new HourlyEmployee ("beth", 40, 20.00); assertEquals (800.00, hourly.getRegularPay()); assertEquals (0.00, hourly.getOvertimePay()); assertEquals (800.00, hourly.getGrossPay()); } // method test2

@Test public void test3() { hourly = new HourlyEmployee ("terry", 41, 20.00); assertEquals (800.00, hourly.getRegularPay()); assertEquals (30.00, hourly.getOvertimePay()); assertEquals (830.00, hourly.getGrossPay()); } // method test3 @Test public void test4() { hourly = new HourlyEmployee ("karen", 50, 10); assertEquals (400.00, hourly.getRegularPay()); assertEquals (150.00, hourly.getOvertimePay()); assertEquals (550.00, hourly.getGrossPay()); } // method test4} // class HourlyEmployeeTest

TESTING PRINCIPLES

Testing can reveal the presence of errors but not the absence of errors.

A method’s tests should be developed before the method

is defined.

In general, methods should be designed to facilitate testing.

Make sure that any boundary conditions are thoroughly tested.

Good testing requires great skepticism.

Exception Handling

A robust program is one that does not terminate abnormally from invalid user input.

An exception is an object created by an unusual condition, such as an attempt at invalid processing

Try to execute a block of code:

try {

… } // try

Catch the exceptions thrown (one catch block per exception type):

catch (Exception e) { … } // catch

For example, suppose we want to develop a method that scans an int and returns the words corresponding to that int. For example, if the int scanned is 537, the String returned is “five three seven”. Here is the method specification:

/** * Returns the words corresponding to the int scanned in. * * @param sc - (a reference to) a Scanner object. * * @return the words corresponding to the int scanned in * from sc, if the token scanned is an int. * Otherwise, return "java.util.InputMismatchException: " + * (the token scanned) + " is not an int.". * */ public String getNumberWords (Scanner sc)

We want to develop and run the test class for this method before we define the method. Then the tests will depend only on the method specification. But how can we run the NumberWordsTest class before we define getNumberWords? Instead of a complete definition, we will use a stub: a one-line definition that is just enough to enable the test class to be run. Of course, we expect all the tests to fail initially. Here is such a stub: public String getNumberWords (Scanner sc) { throw new UnsupportedOperationException(); } // method getNumberWords

Here is a test class for getNumberWords: import org.junit.*; import static org.junit.Assert.*; import org.junit.runner.Result; import static org.junit.runner.JUnitCore.runClasses; import java.util.*; public class NumberWordsTest { public static void main(String[ ] args) { Result result = runClasses (NumberWordsTest.class); System.out.println ("Tests run = " + result.getRunCount() + "\nTests failed = " + result.getFailures()); } // method main

public final static String EXCEPTION = “java.util.InputMismatchException"; protected NumberWords numberWords; @Before public void runBeforeEveryTest( ) { numberWords = new NumberWords(); } // method runBeforeEveryTest @Test public void test805() { assertEquals ("805 is eight zero five ", numberWords.getNumberWords (new Scanner ("805"))); } // method test805

@Test public void test0() { assertEquals ("0 is zero ", numberWords.getNumberWords (new Scanner ("0"))); } // method test0 @Test public void testMinus17() { assertEquals ("-17 is minus one seven ", numberWords.getNumberWords (new Scanner ("-17"))); } // method testMinus17

@Test public void test22z2() { assertEquals (EXCEPTION + ": 22z2 is not an int.", numberWords.getNumberWords (new Scanner ("22z2"))); } // method testtest22z2 @Test public void testx() { assertEquals (EXCEPTION + ": x is not an int.", numberWords.getNumberWords (new Scanner ("x"))); } // method testtestx } // class NumberWordsTest

And here is the rest of the file:

import java.util.*; // for the Scanner class public class NumberWords { public static void main (String[] args) { new NumberWords().run(); } // main public void run() { final String NUMBER_WORDS_MESSAGE = "\nAnswer: "; Scanner sc = new Scanner ("805 17\n22z2\n-7\n395\nx 5\n0"); while (sc.hasNext()) System.out.println (NUMBER_WORDS_MESSAGE + getNumberWords (sc)); } // method run } // class NumberWords

Now we can focus on the definition of the getNumberWords (Scanner scanner) method.

public String getNumberWords (Scanner sc) { try { String[ ] digitWords = {"zero ", "one ", "two ", "three ", "four ", "five ", "six ", "seven ", "eight ", "nine "}; int number = sc.nextInt(), saveNumber = number, digit; String sign = "", numberWords = ""; if (number < 0) { sign = "minus "; number = -number; } // if negative number

if (number == 0) numberWords = "zero "; while (number > 0) { digit = number % 10; number = number / 10; numberWords = digitWords [digit] + numberWords; } // while return Integer.toString (saveNumber) + " is " + sign + numberWords; } // try catch (InputMismatchException e) { return ??????? } // catch InputMismatchException } // method getNumberWords

What should be returned from the catch block? How about

e.toString() + “: “ + Integer.toString (saveNumber) + “ is not an int.”;

There are two problems with this: 1. The variable saveNumber is declared

in the try block, so that variable cannot be used in the catch block.

2. If sc.nextInt( ) fails because the token to be scanned is not an int, the next token to be scanned is that same, non-int token. Then the while loop in the run method would be an infinite loop once “22z2” was scanned.

We can solve both problems by scanning past that non-int token with a call to next():

catch (InputMismatchException e) { return e.toString() + “: “ + sc.next( ) + “ is not an int.”; } // catch InputMismatchException

Exercise: Complete the definition of the following method:

/** * Returns the first three letters of the name scanned. * * @param sc – (a reference to) a Scanner object. * * @returns the first three letters of the name scanned * by sc, if the name has three letters. * Otherwise, returns * “java.lang.StringIndexOutOfBoundsException: * string index out of bounds: 3” * if the name has fewer than 3 letters. * */

public String truncate (Scanner sc) { try { String name = sc.next(); return ??? } // try catch (StringIndexOutOfBoundsException e) { ??? } // catch } // method truncate

Hint: in the String class of the package java.lang,

public String substring (int start, int finish)

returns the substring from index start through index finish-1.

In the truncate method, if we want to allow for the possibility that sc may be a null reference, we would have two catch blocks: try { … } catch (StringIndexOutOfBoundsException e) { … } // catch StringIndexOutOfBoundsException catch (NullPointerException e) { … } // catch NullPointerException

In general, if you want to take the same action for several exceptions, you need only one catch block: catch (Exception e) { return e.toString(); } // catch

This will catch (and return) any exception: InputMismatchException, NoSuchElementException, StringIndexOutOfBoundsException,

NullPointerException, …

Exceptions can be explicitly thrown: if (size == Integer.MAX_VALUE) throw new ArithmeticException (“size too large”);

If an exception is not caught when it is thrown, it is “propagated” back to the calling method. For example, suppose the value scanned in the following is “1Z”.

try { System.out.println (checkPrimality (sc)); } // try catch (InputMismatchException e) { System.out.println (“oooops” + e); } // catch

public boolean checkPrimality (Scanner sc) {

int number = sc.nextInt(); …

} // method checkPrimality

Run-time exceptions, such as

InputMismatchException and

NullPointerException, are automatically propagated. Other exceptions, especially

IOException, are called checked exceptions. They can be propagated only if a throws clause appears right after the method heading.

For example, suppose we have a findMedian method in a Statistics class. try { System.out.println (findMedian (myFileName)); } // try catch (FileNotFoundException e) { System.out.println (e); } // catch FileNotFoundException

/** * Finds median of all int values in file given by filename. * @throws FileNotFoundException – if filename is not

* the name of any file. */ public int findMedian (String fileName) throws FileNotFoundException {

Scanner fileScanner = new Scanner (new File (fileName));

Cultural note: It is considered bad form for the main method to throw any exception – because then the exception will not be caught by your program.

Here is a test of the findMedian method (assume that median is a field in the test class and an instance of the class in which findMedian is defined): @Test (expected = FileNotFoundException.class) public void testBadFileName() { median.findMedian (“?&^%$@*()+_-./”); } // method testBadFileName

You need not propagate exceptions: they can be caught in the same method in which they are thrown. But sometimes a higher-level method is better equipped to handle an exception thrown in a lower-level method. For example, the higher-level method might have access to some feature – such as a Graphical User Interface (GUI) window – that is useful for handling the exception.

File Output PrintWriter printWriter = new PrintWriter

(new BufferedWriter (new FileWriter ("maze.out"))); printWriter.println (“Here is the grid:”);

The output does not immediately go to the file, but is saved in a buffer – a temporary storage area in memory. Output is transferred from the buffer to the file when the buffer is full (and fileWriter.println is called) or when fileWriter.close() is called.

The last method called by an output-file object should be

close().

File input

Scanner fileScanner = new Scanner (new File (filename));

The file name is often read in from the keyboard: Scanner sc = new Scanner (System.in); String fileName = sc.nextLine();

What can go wrong? If fileName is not the name of a file, a FileNotFoundException object will be thrown. But then another string should be read in from the keyboard and the assignment to fileScanner should be repeated.

boolean fileOK = false; while (!fileOK) { try // to create the file scanner { fileName = sc.nextLine(); fileScanner = new Scanner (new File (fileName)); while (fileScanner.hasNext()) { try // to read one int from the file { int score = fileScanner.nextInt(); … // process score } catch (InputMismatchException) { … } } // while fileScanner.hasNext() fileOK = true; } // try catch (FileNotFoundException e) { … } } // while !fileOK

The Java Virtual Machine

also known as

the Java Run-Time Environment

Java Source Code

Java Compiler

Bytecode

Java Virtual Machine

Result of Executing Program

The Java virtual machine handles all run-time aspects of your program. For example:

1. Pre-initialization of fields, just prior to a constructor call (so constant fields cannot be assigned a value after they are declared). This ensures that all fields get initialized.

2. Garbage collection: De-allocation of space for inaccessible objects

The virtual machine handles allocation

by implementing the new operator. What about de-allocation?

Suppose we have a local variable

double[] d = new double [100000];

at the end of the execution of the method, the space for the reference d is deallocated. But what about the space for the object

(100000 doubles)?

Space for that object can be deallocated provided there are no “live” references to the object. The Java virtual machine keeps track of the number of live references to an object.

Visibility modifiers: 1. public – accessible in any class

2. protected – accessible in any class within the

same package as the given class, or in any subclass of the given class

3. default – accessible in any class within the same

package as the given class

4. private – accessible only within the given class

The Object class – the superclass of all classes – has an equals method: public boolean equals (Object obj) { return this == obj; } // method equals

Because this method compares references,not objects, we should override the aboveversion for any class with an equalsmethod.

For example, let’s define an equals method for the FullTimeEmployee class: public boolean equals (Object obj) { // If obj does not reference a FullTimeEmployee // object, return false // return name equals (obj’s name) && // grossPay == obj’s grossPay; } // method equals

For example, let’s define an equals method for the FullTimeEmployee class: public boolean equals (Object obj) { // If obj does not reference a FullTimeEmployee // object, return false // return name equals (obj’s name) && // grossPay == obj’s grossPay; } // method equals Danger: In general, double values should not be tested for equality. For example, 29 * (1000.00 / 29) != 1000.00.

public boolean equals (Object obj) { if (!(obj instanceof FullTimeEmployee)) return false; FullTimeEmployee full = (FullTimeEmployee)obj; return name.equals (full.name) && MONEY.format (grossPay).equals ( MONEY.format (full.grossPay)); } // method equals

Exercise: Define an equals method in the HourlyEmployee class to override the equals method in the Object class. Two HourlyEmployee objects are equal if they have the same name, hours worked and pay rate (but pay rates should not be compared for equality).

Hint: If there is an HourlyEmployee object whose name is “Smith”, with 60 hours worked and a pay rate of $10.00 per hour, that employee should not be equal to another HourlyEmployee named “Smith” with 35 hours worked and a pay rate of $20.00 per hour. The gross pay is $700.00 in both cases.