Solutions to Exercises



Chapter 1: Computers and Programming

What Is a Computer?

1. An army soldier is a computer who follows orders given by her superior. A cook is a computer who cooks hot food on command. A store clerk is a computer who fetches the merchandise you request. A vending machine is a computer that produces a product in response to the money you submit. A car is a computer that moves in response to your orders. Of course, there are many other examples.

3. The input device for a calculator is the keypad upon which you type numerals and arithmetic operations. The output device is the display or paper tape upon which answers appear.

Computer Programming

2. A left-to-right execution of the example would follow these steps:

  1. add 3 to 2, giving 5
  2. subtract 1 from it, giving 4
  3. add 6 to 5, giving 11
  4. subtract 11 from 4, giving -7

3. The steps for solving for x might go as follows:

  1. add x to both sides of the equation, giving 3y - x + x = 3 + 2x + x
  2. cancel -x + x, giving 3y = 3 + 2x + x
  3. add 2x + x, giving 3y = 3 + 3x
  4. subtract 3 from both sides, giving 3y - 3 = 3 + 3x - 3
  5. reorder the last two arguments, giving 3y - 3 = 3 - 3 + 3x
  6. cancel 3 - 3, giving 3y - 3 = 3x
  7. Divide both sides by 3, giving (3y - 3) / 3 = 3x / 3
  8. Cancel the division by 3: (3y - 3) / 3 = x.
The answer can be further simplified to x = y - 1.

Chapter 2: Simple Java Applications

How the Application Works

1.

/** Name  prints my name on the console */
public class Name
{ public static void main(String[] args)
  { System.out.println("Fred Mertz"); }
}

2.

/** Name  prints my name, in two lines, on the console */
public class Name
{ public static void main(String[] args)
  { System.out.println("Fred");
    System.out.println("Mertz");
  }
}

3. Both programs have a class with a main method; the method sends messages to System.out.

Syntax and Semantics

1. clas should be class; the parenthesis before System should be a bracket, {; the period should be a semicolon; there is an extra right bracket at program's end.

2. The object, System.out is located, and within that object, the println method is located, and the argument, "Fred", is given to the println method. The method displays the argument (less its enclosing double quotes) on the console.

How One Object Creates Another

Each time the application is executed, a unique, exact time is displayed.

Chapter 3: Arithmetic and Variables

Integer Arithmetic

1.

6 * ((-2 + 3) * (2 - 1))
=>  6 * (-1 * (2 - 1))
=>  6 * (-1 * 1)  =>  6 * -1  =>  -6
The application that computes this answer would appear as follows:
public class Test
{ public static void main(String[] args)
  { System.out.println(6 * ((-2 + 3) * (2 - 1))); }
}

2.

6 * (-2 + 3) * (2 - 1) 
=>  6 * -1 * (2 - 1)
=>  -6 * (2 - 1)  =>  -6 * 1  => -6

3.

6 * -2 + 3 * (2 - 1) 
=>  -12 + 3 * (2 - 1)
=>  -12 + 3 * 1  =>  -12 + 3  =>  -9

4.

6 * -2 + 3 * 2 - 1 
=>  -12 + 3 * 2 - 1
=>  -12 + 6 - 1  =>  -6 - 1  =>  -7

Named Quantities: Variables

1. Only the four initialization statements change:

public class TotalVariables
{ public static void main(String[] args)
  { int quarters = 3;
    int dimes = 0;
    int nickels = 12;
    int pennies = 0;

    System.out.println("For these quantities of coins:");
    System.out.print("Quarters = ");
    System.out.println(quarters);
    System.out.print("Dimes = ");
    System.out.println(dimes);
    System.out.print("Nickels = ");
    System.out.println(nickels);
    System.out.print("Pennies = ");
    System.out.println(pennies);
    System.out.print("The total is ");
    System.out.println( (quarters * 25) + (dimes * 10)
                           + (nickels * 5) + (pennies * 1) );
  }
}

2.

/** Total computes the amount of change I have, based on the values
  * named by the four variables,  quarters, dimes, nickels, and  pennies. */
public class TotalVariables
{ public static void main(String[] args)
  { int quarters = 4;
    int dimes = 0;
    int nickels = 1;
    int pennies = 1;

    System.out.println("For these quantities of coins:");
    System.out.println("Quarters = " + quarters + ", worth "
                        + (quarters * 25) + " cents");
    System.out.println("Dimes = " + dimes + ", worth "
                        + (dimes * 10) + " cents");
    System.out.println("Nickels = " + nickels + ", worth "
                        + (nickels * 5) + " cents");
    System.out.println("Pennies = " + pennies + ", worth "
                        + pennies + " cents");
    System.out.println("The total is "
         + ((quarters * 25) + (dimes * 10) + (nickels * 5) + (pennies * 1))
         + " cents");
  }
}

3. The problem lies in the statement,

System.out.println("The total is $" + (total / 100) + "." + (total % 100));
Since total has value 106, then total % 100 computes to 6, and 6 (not 06) is printed as the cents amount.

Variables Can Vary: Assignments

1.

public class MakeChange
{ public static void main(String[] args)
  { int dollars = 3;
    int cents = 46;
    int money = (dollars * 100) + cents;
    System.out.println( "five-dollar bills = " + (money/500) );
    money = money % 500;
    System.out.println( "one-dollar bills = " + (money/100) );
    money = money % 100;
    // the remainder of the program is the same as that in Figure 3....
}

2. 6/4 => 1; 6%4 => 2; 7/4 => 1; 7%4 => 3; 8/4 => 2; 8%4 => 0; 6/-4 => -1; -6%4 => -1; 6%-4 => 2.

3. The fundamental property about division on integers is: Given a quotient, q, and a divisor, d, when we compute that q DividedBy d gives a dividend of v and a remainder of r, then we must have that q = (v*d) + r. (Consider the example, 346 DividedBy 25; we get a dividend of 13 and a remainder of 21, and we have that 346 = (13*25) + 21.)

In general, for any money amount, money, regardless of the dividend, v, and remainder, r, that we compute, we must have that money = (v*25) + r. So to reduce the value of money by subtracting the quarters just given out, we calculate: money = money - (v*25). But the amount that remains must be r ! Hence money = money % 25 calculates the same result.

4.

int my_money = 12;
my_money = my_money - 5;
my_money = my_money * 2;
my_money = 1;
System.out.println(my_money);

5.

   Exercise3
  -----------------
  |  main
  |  { >int x = 12;
  |    int y = x + 1;
  |    x = x + y;
  |    y = x;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 12 |
  |             ----
  |    >int y = x + 1;
  |    x = x + y;
  |    y = x;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 12 |
  |             ----
  |    >int y = 12 + 1;
  |    x = x + y;
  |    y = x;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 12 |
  |             ----
  |    int y ==| 13 |
  |             ----
  |    >x = x + y;
  |    y = x;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 12 |
  |             ----
  |    int y ==| 13 |
  |             ----
  |    >x = 12 + 13;
  |    y = x;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 13 |
  |             ----
  |     ...
  |    >y = x;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 13 |
  |             ----
  |     ...
  |    >y = 25;
  |    System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 25 |
  |             ----
  |     ...
  |    >System.out.println(x + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 25 |
  |             ----
  |     ...
  |    >System.out.println(25 + " equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 25 |
  |             ----
  |     ...
  |    >System.out.println("25 equals " + y);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 25 |
  |             ----
  |     ...
  |    >System.out.println("25 equals " + 25);
  |  }
   Exercise3
  -----------------
  |  main       ----
  |  { int x ==| 25 |
  |             ----
  |    int y ==| 25 |
  |             ----
  |     ...
  |    >System.out.println("25 equals 25");
  |  }
and the phrase, 25 equals 25, appears on the console.

Arithmetic with Fractions: Doubles

1. First, elementary algebra lets us deduce from F = (9/5)C + 32 that C = (5F - 160)/9. The resulting program is

/**  FahrenheitToCelsius converts an input Fahrenheit value to Celsius.
  *    input: the degrees Fahrenheit, an integer received from the command line
  *    output: the degrees Celsius, a double */
public class FahrenheitToCelsius
{ public static void main(String[] args)
  { int f =  ... ;  // place a Fahrenheit value here
    double c = ((5.0*f) - 160) / 9.0;
    System.out.println( "Celsius = " + c ); }
}

2.

/**  KilometersToMiles converts an input kilometers value to miles.
  *    input: the kilometers, an integer received from the command line
  *    output: the equivalent miles.  */
public class KilometersToMiles
{ private static BasicFrameWriter writer = new BasicFrameWriter();

  public static void main(String[] args)
  { int k = ... ;  // place a kilometers value here
    double m = 0.62137 * k;
    writer.println(k + " kilometers");
    writer.println("are " + m + " miles.");
  }
}

3.1.

(5.3 + 7) / 2.0
=>  (5.3 + 7.0) / 2.0 
=>  12.3 / 2.0  =>  6.15

3.2.

(5.3 + 7) / 2 
=>  (5.3 + 7.0) / 2
=>  12.3 / 2  =>  12.3 / 2.0  =>  6.15

3.3.

5.3 + (7 / 2) 
=>  5.3 + 3  =>  5.3 + 3.0  =>  8.3

3.4.

(1.0 + 2) + ((3%4)/5.0) 
=>  (1.0 + 2.0) + ((3%4)/5.0)
=>  3.0 + ((3%4)/5.0)
=>  3.0 + (3 / 5.0)
=>  3.0 + (3.0 / 5.0)  =>  3.0 + 0.6  =>  3.6

Booleans

1.

public class CompareTemps
{ public static void main(String[] args)
  { double C = 40.0;
    double F = 40.0;
    double C_converted = ((9.0/5.0) * C) + 32;
    System.out.print(C + " Celsius warmer than " + F + " Fahrenheit?  ");
    System.out.println(C_converted > F);
  }
}

2.

public class CompareCoins
{ public static void main(String[] args)
  { int dimes = 4;
    int nickels = 6;
    System.out.print(dimes + " dimes worth less than "
                   + nickels + " nickels?  ");
    System.out.println((dimes * 10) < (nickels * 5));
  }
}

3.1.

(3 * 2) >= (-9 - 1) 
=>  6 >= (-9 - 1)
=>  6 >= -10  =>  true

3.2.

3 * 2 != 5.5 + 0.4 + 0.1 
=>  6  != 5.5 + 0.4 + 0.1 
=>  6  != 5.9 + 0.1 
=>  6  != 6.0  =>  6.0 != 6.0  => false

Operator Precedences

1.1 6 * -2 + 3 / 2 - 1 => -12 + 3 / 2 - 1 => -12 + 1 - 1 => -11 -1 => -12.

1.2. (5.3 + (7 / 2)) + 0.1

1.3 ((((3*2)%4)/5.0)*2)*3

Strings and Characters

1.1.

2 + ("a" + " ") + "bc" 
=>  2 + "a " + "bc" 
=>  "2a " + "bc"  =>  "2a bc"

1.2.

1 + "" + (2 + 3) 
=>  "1" + (2 + 3)  =>  "1" + 5  =>  "15"

1.3.

1 + 2 + "" + 3
=>  3 + "" + 3  =>  "3" + 3  =>  "33"

1.4.

1 + 2 + 3 + ""
=>  3 + 3 + ""  =>  6 + ""  =>  "6"

1.5.

1 + "" + (2 + 3)
=>  "1" + (2 + 3)  =>  "1" + 5  =>  "15"

2.1 false, because

t.equals(u)  =>  "abc ".equals("ab")  =>  false

2.2 true, because

u.charAt(1) == t.charAt(1)
=>  "ab".charAt(1) == t.charAt(1)
=>  'b' == t.charAt(1)
=>  'b' == "abc ".charAt(1)
=>  'b' == 'b'  =>  true

2.3 2, because

t.length() - u.length()
=>  "abc ".length() - u.length()
=>  4 - u.length()
=>  4 - "ab".length()  =>  4 - 2  =>  2

2.4 abc, because

u + 'c'  =>  "ab" + 'c'  =>  "abc"

2.5 abc, because

t.trim()  =>  "abc ".trim()  =>  "abc"

2.6 ABC , because

t.toUpperCase()  =>  "abc ".toUpperCase()  =>  "ABC "

Data Type Checking

1.1. 5.3 + (7 / 2) will calculate to an answer that is a double.

1.2. 2.3 * (1 == 2) contains a data-type error, because a double cannot be multiplied by a boolean.

1.3. 3.1 < 2 < 3 contains a data-type error, because it must be read as (3.1 < 2) < 3, which states that a boolean is being compared by less-than to an integer, and this is not allowed.

1.4. "a " + 1 + 2 will compute to a String.

1.5. ("a " + 1) * 2 contains a data-type error, because it is illegal to multiply a string by an integer.

2.

int x = 3.5;  // cannot assign a double to an int variable
double d = 2;
String s = d; // cannot assign a number to a string variable
d = (d > 0.5); // cannot assign a boolean answer to a double
System.out.println(s * 3); // cannot multiply a string by an int

Input via Program Arguments

2.

public class MakeChange
{ public static void main(String[] args)
  { int dollars = new Integer(args[0]).intValue();
    int cents = new Integer(args[1]).intValue();
    int money = (dollars * 100) + cents;
    System.out.println("quarters = " + (money / 25));
    money = money % 25;
    System.out.println("dimes = " + (money / 10));
    money = money % 10;
    System.out.println("nickels = " + (money / 5));
    money = money % 5;
    System.out.println("pennies = " + money); }
}

3.

/**  KilometersToMiles converts an input kilometers value to miles.
  *    input: the kilometers, an integer received from the command line
  *    output: the equivalent miles.  */
public class KilometersToMiles
{ private static BasicFrameWriter writer = new BasicFrameWriter();

  public static void main(String[] args)
  { int k = new Integer(args[0]).intValue();
    double m = 0.62137 * k;
    writer.println(k + " kilometers");
    writer.println("are " + m + " miles.");
  }
}

4.

public class Test
{ public static void main(String[] args)
  { String s = args[0];
    int i = new Integer(args[2]).intValue();
    double d = new Double(args[3]).doubleValue();
  }
}

5.

public class Test
{ public static void main(String[] args)
  { int i = new Integer(args[0]).intValue();
    int j = new Integer(args[1]).intValue();
    System.out.println( i > j );
  }
}

Chapter 4: Input, Output, and State

Interactive Input

1.

public class Test
{ public static void main(String[] args)
  { String name = JOptionPane.showInputDialog("Please type your name:");
    String a = JOptionPane.showInputDialog("Please type your age: ");
    int age = new Integer(a).intValue();
  }
}

2.

public class Square
{ public static void main(String[] args)
  { String n = JOptionPane.showInputDialog("Please type an integer: ");
    int i = new Integer(n).intValue();
    System.out.println(i + " squared is " + (i * i));
  }
}

Customizing Panels with Inheritance

1. The paintComponent method is changed to

public void paintComponent(Graphics g)
{ g.setColor(Color.red);
  int left_edge = 105;   // the left border where the shapes appear
  int bottom = 130;      // the bottom where the shapes lie
  // draw a rectangle:
  int width = 90;
  int depth = 60;
  g.drawRect(left_edge, bottom - depth, width, depth);
  // draw a filled circle:
  g.setColor(Color.black);
  int diameter = 40;
  g.fillOval(left_edge, bottom - diameter, diameter, diameter);
}

2. and 3. The controller is changed to

import javax.swing.*;
import java.awt.*;
/** FrameTest3 displays a colorful graphics window */
public class FrameTest3
{ public static void main(String[] args)
  { JFrame my_frame = new JFrame();
    // insert a new panel into the frame:
    my_frame.getContentPane().add(new MyPanel());
    // set the title bar at the top of the frame:
    my_frame.setTitle("MyFrameWriter");
    // an easy way to color the backgrond of the entire window:
    my_frame.setBackground(Color.yellow);
    int frame_width = 400;
    int frame_height = 400;
    my_frame.setSize(frame_width, frame_height);
    my_frame.setVisible(true);
    System.out.println("Frame has appeared!");
  }
}

4. Change the fillOval statement to read g.fillOval(left_edge, bottom - diameter, diameter * 2, diameter).

5. Change the initialization of left_edge to int left_edge = 210; of course, this repositions the circle as well as the rectangle.

Graphics Windows as Output Views

1. In Figure 14, change the paintComponent method's last four statements to read

// draw the minutes' hand red, with a width of 5 degrees:
g.setColor(Color.red);
g.fillArc(left_edge + 5, top + 5, diameter - 10, diameter - 10,
          minutes_angle, 5);
// draw the hours' hand blue, with a width of -8 degrees:
g.setColor(Color.blue);
g.fillArc(left_edge + 5, top + 5, diameter - 10, diameter - 10,
          hours_angle, -10);

2. In Figure 14, change the beginning of the paintComponent method to read,

public void paintComponent(Graphics g)
{ GregorianCalendar time = new GregorianCalendar();
  int minutes = time.get(Calendar.MINUTE);
  int hours = time.get(Calendar.HOUR);
  int minutes_angle = 90 - (minutes * 6);
  int hours_angle = 90 - (int)((hours + (minutes/60.0)) * 30);
  // draw the clock as a black circle:
   ...
}

3. Add these two statements to the end of the paintComponent method in Figure 14:

g.setColor(Color.black);
g.drawString(time.get(Calendar.HOUR) + ":" + time.get(Calendar.MINUTE),
             90, 180);
Alas, when the minutes amount is 9 or less, the leading zero is lost, e.g., 11:05 prints 11:5. We repair this problem in Chapter 6.

Objects with State: Field Variables

1.

import java.awt.*;
import javax.swing.*;
/** EggWriter creates a graphics window that displays an egg.  Each time
  * the window is repainted, the egg shrinks by half.   */
public class EggWriter extends JPanel
{ private int WIDTH = 300;   // width of the entire frame
  private int HEIGHT = 200;  // height of the entire frame
  private Color BACKGROUND_COLOR = Color.yellow;  // color to paint frame

  private int egg_width = 600;  // width of egg to draw; must be nonnegative
  private int egg_height = 400; // height of egg to draw; must be nonnegative

  /** Constructor EggWriter creates the window and makes it visible */
  public EggWriter()
  { JFrame my_frame = new JFrame();
    my_frame.getContentPane().add(this);
    my_frame.setTitle("EggWriter");
    my_frame.setSize(WIDTH, HEIGHT);
    my_frame.setVisible(true);
  }

  /** paintComponent  draws the egg.
    * @param g - the ``graphics pen'' that draws the egg */
  public void paintComponent(Graphics g)
  { g.setColor(BACKGROUND_COLOR);
    g.fillRect(0, 0, WIDTH, HEIGHT);  // ``erase'' the entire background

    int left_border = 10;   // horizontal position of the egg
    int baseline = 190;     // where to lay the egg
    g.setColor(Color.pink);
    g.fillOval(left_border, baseline - egg_height, egg_width, egg_height);
    g.setColor(Color.black);
    g.drawOval(left_border, baseline - egg_height, egg_width, egg_height);

    // reset the fields so the egg shrinks the next time it is painted:
    egg_width = egg_width / 2;
    egg_height = egg_height / 2;
  }
}
Start the application with this:
public class ShowEgg
{ public static void main(String[] args)
  { new EggWriter(); }
}

Testing a Program that Uses Input

``Black box'' inputs are those that a typical, not-so-careful user might type. For the change-making program, we might try these black-box dollars-and-cents values:

``White-box'' inputs are created to help us understand and analyze specific statements within the program. The change-making program does its computations with the modulo operation, so we might create these inputs, which exercise the statement, money = money % 25, and show what happens when the change can be computed with quarters only and with quarters and some other coins: The other modulo statements in the program can be similarly exercised.

Chapter 5: Component Structure: Method and Class Building

Basic Public Methods

1. The modified program prints two butterflies.

2. The following are printed: a newline, a bee, a newline, a butterfly, a ladybug. Two objects are created, one for each occurrence of the new keyword.

3.

public class TestHelperClass
{ public static void main(String[] args)
  { HelperClass helper = new HelperClass();
    helper.computeSquareRoot();
    helper.computeSquareRoot();
  }
}

4.

/** readNameAndDisplayItsLength  reads one name and displays the
  *  name with its length  */
private static void readNameAndDisplayItsLength()
{ String s = JOptionPane.showInputDialog("Type a name:");
  JOptionPane.showMessageDialog(null, s + " has length " + s.length());
}

Constructor Methods

1.

A
B
B
A
B
Two objects are constructed, hence A prints twice. The final assignment does not construct an object---it merely copies the address held by p into q.

2.

1
2
1
3
4
5
Two objects, with distinct count fields, are constructed by the example.

Parameters to Methods

1. The application prints FredFred FredFred because the actual parameter computes to "FredFred". Note there is no connection whatsoever between the two variables named s.

2.

import javax.swing.*;
/** NameClass remembers a name and prints information about it. */
public class NameClass
{ private String name;  // the name that is remembered

  /** Constructor NameClass initializes a NameClass object 
    * @param n - the name that is to be remembered  */
  public NameClass(String n)
  { name = n; }

  /** printName prints the name remembered by this object */
  public void printName()
  { System.out.println(name); }

  /** displayLength prints the integer length of the name remembered.
    * (Hint: use the  length  method from Table 5, Chapter 3.)  */
  public void displayLength()
  {  System.out.println(name.length()); }
 }

Forms of Parameters

1.1.

311.0
A12
A9
The first invocation has actual parameters "3" and 5.5, causing the string "3" to be appended to 11.0. The second invocation has actual parameters "A" and 4; the third has parameters "A" and 3.

1.2. Here are the errors located by the Java compiler:

2.

/** RunningTotal helps a child total a sequence of numbers */
public class RunningTotal
{ private int total;  // the total of the numbers added so far
     
  /** Constructor RunningTotal initializes the object */
  public RunningTotal()
  { total = 0; }
     
  /** addToTotal  adds one more number to the running total
    * @param num - the integer to be added to the total  */
  public void addToTotal(int num)
  { total = total + num; }

  /** printTotal  prints the current total of all numbers added so far */
  public void printTotal()
  { System.out.println(total); }
}

3. Class ClockWriter is revised to read as follows:

  public void paintComponent(Graphics g)
  { int left_edge = 50;  
    int top = 50;
    int diameter = 100;
    GregorianCalendar time = new GregorianCalendar();
    paintClock(time.get(Calendar.HOUR), time.get(Calendar.MINUTE),
               left_edge, top, diameter, g);
  }

  /** paintClock paints a clock with the time
    * @param hours - the current hours, an integer between 1 and 12
    * @param minutes - the current minutes, an integer between 0 and 59
    * @param x - the upper left corner where the clock should appear
    * @param y - the upper right corner where the clock should appear  
    * @param diameter - the clock's diameter 
    * @param g - the graphics pen used to paint the clock  */
  public void paintClock(int hours, int minutes, int x, int y,
                         int diameter, Graphics g)
  { int minutes_angle = 90 - (minutes * 6);
    int hours_angle = 90 - (hours * 30);
    // draw the clock as a black circle:
    int left_edge = 50;  
    int top = 50;
    int diameter = 100;
    g.setColor(Color.black);
    g.drawOval(x, y, diameter, diameter);
    // draw the minutes' hand red, 10 pixels smaller, with a width of 5 degrees:
    g.setColor(Color.red);
    g.fillArc(x + 5, y + 5, diameter - 10, diameter - 10,
              minutes_angle, 5);
    // draw the hours' hand blue, 50 pixels smaller, with a width of -8 degrees:
    g.setColor(Color.blue);
    g.fillArc(x + 25, y + 25, diameter - 50, diameter - 50, hours_angle, -8);
  }

Case Study: General Purpose Output Frame

1. The application displays the first input sentence at position 60, 100. The second input sentence appears at the same position and then is immediately repositioned at 0, 190. Almost immediately after that, the sentence is printed twice at 0, 190. To the user's eyes, the first and last displays are all that are seen.

2.

import java.text.*;
public class SquareRoot
{ public static void main(String[] args)
  { String s = JOptionPane.showInputDialog("Please type an integer:");
    int num = new Integer(s).intValue();
    double d = Math.sqrt(num);
    String answer = new DecimalFormat("0.000000").format(d);
    new MyWriter.writeSentence("the square root of " + num + " is " + answer);
  }
}

3.

import java.awt.*;
import javax.swing.*;
/** MyWriter creates a graphics window that displays a sentence */
public class MyWriter extends JFrame
{ private int width;  // the frame's width
  private int height; // the frame's height

  private String sentence = ""; // holds the sentence to be displayed
  private String sentence2 = "";  // second sentence to be displayed

  private int x_position;  // x-position of sentence
  private int y_position;  // y-position of sentence
  private int increment = 20;  // where second sentence prints under first

  /** Constructor MyWriter creates the window and makes it visible
    * @param w - the window's width
    * @param h - the window's height  */
  public MyWriter(int w, int h)
  { ... as before ... }

  /** paint paints the window
    * @param g - the ``graphics pen'' that draws the items onto the window */
  public void paint(Graphics g)
  { g.setColor(Color.red);
    g.drawString(sentence, x_position, y_position);
    g.drawString(sentence2, x_position, y_position + increment);
  }

  /** writeSentence displays a new string in the window
    * @param s - the sentence to be displayed  */
  public void writeSentence(String s)
  { ... as before ... }

  /** repositionSentence redisplays the existing sentence in a new position
    * @param new_x - the new horizontal starting position
    * @param new_y - the new vertical starting position  */
  public void repositionSentence(int new_x, int new_y)
  { ... as before ... }

  /** writeSecondSentence displays a sentence,  t,  underneath the first
    * sentence in the window.
    * @param t - the second sentence to be displayed  */
  public void writeSecondSentence(String t)
  { sentence2 = t;
    this.repaint();
  }
}

4.

import java.awt.*;
import javax.swing.*;
/** TestWriter displays up to three lines of text in a graphics window */
public class TextWriter extends JPanel
{ private int width;  // the frame's width
  private int height; // the frame's height

  private String sentence1 = "";
  private String sentence2 = "";
  private String sentence3 = "";
  private int x_position;  // x-position of sentences
  private int y_position;  // y-position of sentences
  private int increment = 20;  // skip between sentences

  /** Constructor TextWriter creates the window and makes it visible
    * @param w - the window's width
    * @param h - the window's height  */
  public TextWriter(int w, int h)
  { width = w;
    height = h;
    x_position = width / 5;  // set the sentence's position
    y_position = height / 2;
    JFrame my_frame = new JFrame();
    my_frame.getContentPane().add(this);
    my_frame.setTitle("TextWriter");
    my_frame.setSize(width, height);
    my_frame.setVisible(true);
  }

  /** paintComponent paints the window
    * @param g - the ``graphics pen'' that draws the items onto the window */
  public void paintComponent(Graphics g)
  { g.setColor(Color.red);
    g.drawString(sentence1, x_position, y_position);
    g.drawString(sentence2, x_position, y_position + increment);
    g.drawString(sentence3, x_position, y_position + (2 * increment));
  }

  public void print1(String s)
  { sentence1 = sentence1 + s;
    this.repaint();
  }

  public void reset1(String s)
  { sentence1 = s;
    this.repaint();
  }

  // the remaining methods resemble the two above
}

Results from Methods: Functions

1.

4
4 5.5
*45

2.

public class TemperatureConvertor
{ public TemperatureConvertor() { }

  public double celsiusIntoFahrenheit(double c)
  { return ((9.0 / 5.0) * c) + 32; }

  public double fahrenheitIntoCelsius(double f)
  { return (5 * (f - 32)) / 9.0; }
}

3.1

/** kilometersToMiles converts a kilometers amount into the nearest miles amount
  * @param k - the kilometers amount
  * @return the corresponding miles amount  */
public double kilometersToMiles(double k)
{ return (0.62137 * k); }

3.3

/** isDivisibleByNine checks if its argument is divisible by 9 with no remander.
  * @param arg - the argument to be checked
  * @return true, if it is divisible by 9; return false, otherwise.  */
public boolean isDivisibleByNine(int arg)
{ return ((arg % 9) == 0); }

3.4 The obvious solution goes

/** pay  computes the weekly pay of an employee
  * @param name - the employee's name
  * @param hours - the hours worked for the week
  * @param payrate - the hourly payrate
  * @return a string that includes the name followed by "$" and the pay amount*/
public String pay(String name, int hours, double payrate)
{ double pay = hours * payrate;
  return (name + " $" + pay);
}

4.

/** Areas prints the areas of three circles */
public class Areas
{ public static void main(String[] args)
  { Formulas c = new Formulas();
    System.out.println("For radius 4, area = " + c.areaOf(4));
    System.out.println(c.areaOf(8));
    System.out.println(c.areaOf(19) + " is the area for radius 19");
  }
}

public class Formulas
{ public Formulas() { }

  /** areaOf calculates the area within a circle
    * @param radius - the circle's radius
    * @return the area  */
  public double areaOf(int radius)
  { return (Math.PI * radius * radius); }
}

Private Methods

2. Without changing any of the existing coding of class StackedEggsWriter, add these methods:

/** setEggSize1 sets the size of the bottom egg
  * @param size - the size (width) of the egg */
public void setEggSize1(int size)
{ egg1_size = size;  repaint(); }

public void setEggSize2(int size)
{ egg2_size = size;  repaint(); }

public void setEggSize3(int size)
{ egg3_size = size;  repaint(); }

4.

import java.awt.*;
import javax.swing.*;
/** Circles draws three concentric circles */
public class Circles2 extends JPanel
{ private int diameter = 60;  // diameter of largest circle to draw
  int x_pos = 100;  // x-position of center of circle
  int y_pos = 100;  // y-position of center of circle

  public Circles2()
  { JFrame my_frame = new JFrame();
    my_frame.getContentPane().add(this);
    my_frame.setTitle("TextWriter");
    my_frame.setSize(200, 200);
    my_frame.setVisible(true);
  }

  public void paintComponent(Graphics g)
  { g.setColor(Color.black);
    paintOval(diameter, g);
    paintOval(diameter / 2, g);
    paintOval(diameter / 4, g);
  }

  public void paintOval(int d, Graphics g)
  { int radius = d / 2;
    g.drawOval(x_pos - radius, y_pos - radius, d, d);
  }
}

Chapter 6: Control Structure: Conditional Statements

Conditional Statements

1.1 Not equal.

1.2

less
less

2.1. The test of a conditional must be boolean-typed, but x has type int.

2.2. Parentheses are required around the test expression, x>0. Also, x = 2 must be terminated by a semicolon.

2.3. The test expression, x = 0 is incorrect---it should be written x == 0. Also, the semicolon preceding the else must be removed.

4.1.

public void printEven(int a)
{ if ( (a % 2) == 0 )
       { System.out.println("EVEN"); }
  else { System.out.println("ODD"); }
}

4.2.

public int minus(int arg1, int arg2)
{ int answer = -1;
  if ( arg1 >= 0 )
     { if ( arg1 >= arg2 )
          { answer = arg1 - arg2; }
     }
  return answer;
}

4.3.

public int div(int arg1, int arg2)
{ int answer = 0;
  if ( arg2 == 0 )
       { System.out.println("div error: division by zero"); }
  else { answer = arg1 / arg2; }
  return answer;
}

Nested Conditional Statements

1.

b
c
d
e

2. One set of tests might be: -2 0, 2 -2, 2 102, and 2 2.

3.1.

import javax.swing.*;
  ...
public int convertToSeconds(int hours, int minutes)
{ int seconds = -1;
  if ( hours < 0 )
       { JOptionPane.showMessageDialog(null,
          "error: negative hours: " + hours);
       }
  else { if ( minutes < 0 )
              { JOptionPane.showMessageDialog(null,
                  "error: negative minutes: " + minutes);
              }
         else { if ( minutes > 59 )
                     { JOptionPane.showMessageDialog(null,
                         "error: bad minutes: " + minutes);
                     }
                else { seconds = ((hours * 60) + minutes) * 60; }
              }
       }
  return seconds;
}

3.2.

import javax.swing.*;
  ...
public int convertToSeconds2(int hours, int minutes)
{ boolean ok = true;  // remembers whether input data is acceptable
  int seconds = -1;
  if ( hours < 0 )
     { JOptionPane.showMessageDialog(null,
        "error: negative hours: " + hours);
       ok = false;
     }
  if ( minutes < 0 )
     { JOptionPane.showMessageDialog(null,
         "error: negative minutes: " + minutes);
       ok = false;
     }
  if ( minutes > 59 )
     { JOptionPane.showMessageDialog(null,
         "error: bad minutes: " + minutes);
       ok = false;
     }
  if ( ok )
     { seconds = ((hours * 60) + minutes) * 60; }
  return seconds;
}

4. Consider the arguments, -2 abc; Figure 2 reports an error with the first argument and does no more computation; Figure 4 throws an exception and immediately terminates.

To make Figure 4 behave identically to Figure 2, the third statement of the main method must be changed to read, say, int cents = -1, and the assignment, cents = new Integer(args[1]).intValue(), must be moved inside the then-arm of the second conditional statement.

Relational Operations

1.1.

(x > 1) && ((2*x) <= y)  
=>  (2 > 1) && ((2*x) <= y)
=>  true && ((2*x) <= y)
=>  true && ((2*2) <= y)
=>  true && (4 <= y)
=>  true && (4 <= 3.5)
=>  true && (4.0 <= 3.5)
=>  true && false  =>  false

1.2 !(x == 1) => !(2 == 1) => !false => true.

2. minus(3, 2) returns 1; minus(2, 3) returns -1; minus(-4, -5) returns -1. minus(4, -5) returns 9.

3.1.

public boolean isSmallPrime(int i)
{ boolean answer;
  if ( i == 2 || i == 3 || i == 5 || i == 7 )
       { answer = true; }
  else { answer = false; }
  return answer;
}
Note: the method's body can be replaced by this single statement:
return ( i == 2 || i == 3 || i == 5 || i == 7 );

3.2

public double divide(double x, double y)
{ double answer = 0;
  if ( x >= 0  &&  y != 0 )
     { answer = x / y; }
  return answer;
}

Uses of Conditionals

1. Say that twelveHourClock is found in class TimeConvertor:

public class TestTime
{ public static void main(String[] args)
  { TimeConvertor t = new TimeConvertor();
    String time = t.twelveHourClock(9, 45);
    System.out.println(time);
    // Indeed, it is simpler to test each case in just one statement each:
    System.out.println(t.twelveHourClock(23, 59));
    System.out.println(t.twelveHourClock(23, 59));
    System.out.println(t.twelveHourClock(0, 00));
    System.out.println(t.twelveHourClock(50, 50));
    System.out.println(t.twelveHourClock(-12, -12));
    System.out.println(t.twelveHourClock(24, 0));
  }
}

2. Say that twelveHourClock is found in class TimeConvertor:

import java.util.*;
public class TestTime
{ public static void main(String[] args)
  { TimeConvertor t = new TimeConvertor();
    GregorianCalendar time = new GregorianCalendar();
    String s =
       t.twelveHourClock(time.get(Calendar.HOUR_OF_DAY),
                         time.get(Calendar.MINUTE));
    System.out.println(s);
  }
}
3.
/** translateGrade converts a numerical score to a letter grade.
  * @param score - a numerical score that must be in the range 0..100
  * @return a letter grade based on this scale:
  *   100..90 = "A"; 89..80 = "B"; 79..70 = "C"; 69..60 = "D"; 59..0 = "F" */
public String translateGrade(int score)
{ String grade = "F";
  if ( score >= 90 )
       { grade = "A"); }
  else { if ( score >= 80 )
            { grade = "B"; }
         else { if ( score >= 70 )
                     { grade = "C"; }
                else { if ( score >= 60 )
                            { grade = "D"; }
                       // else the grade stays at "F"
                      }
              }
       }
  return grade;
}

Case Study: Bank Accounts Manager

1.1.

public class TestAccount
{ public static void main(String[] args)
  { BankAccount b = new BankAccount(0);
    b.deposit(1000);
    boolean ok = b.withdraw(700);
    System.out.println("balance = " + b.balanceOf());
  }
}

1.2.

public class TestReader
{ public static void main(String[] args)
  { BankReader r = new BankReader();
    String s = r.readCommand("Type a command: ");
    System.out.println("Command was: " + s);
    int i = r.readAmount();
    System.out.println("Amount was: " + i);
  }
}

1.3.

public class TestAccountWriter
{ public static void main(String[] args)
  { BankAccount b = new BankAccount(0);
    BankWriter w = new BankWriter("Writer", b);
    b.deposit(1000);
    w.showTransaction("deposit", 1000);
    boolean ok = b.withdraw(700);
    w.showTransaction("withdrawal", 700);
  }
}

1.4.

public class TestAll
{ public static void main(String[] args)
  { BankAccount b = new BankAccount(0);
    BankReader r = new BankReader();
    BankWriter w = new BankWriter("Writer", b);
    String s = r.readCommand("Type something:");
    int i = r.readAmount();
    b.deposit(i);
    w.showTransaction(s, i);
  }
}

2. The new method is added to the model:

public class BankAccount
{ ...
  public int depositInterest(double rate)
  { int amount = 0;
    if ( rate < 0.0 || rate > 1.0 )
         { System.out.println("BankAccount error: bad interest rate"); }
    else { amount = (int)(rate * balance);
           deposit(amount); 
         }
    return amount;
  }
}
The controller is revised to read:
public class AccountController
{ ...
  public void processTransactions()
  { ...
    else if ( command.equals("I") )
         { double rate = reader.readInterestRate();
           amount = account.depositInterest(rate);
           writer.showTransaction("Interest deposit of ", amount);
         }
    ...
  }
}
And this requires a new method, readInterestRate, in the input view:
public class BankReader
{ ...
  /** readInterestRate reads an interest rate in per cent
    * @return the rate, converted into a fraction */
  public double readInterestRate()
  { ... } // program this to look similar to  readAmount
}

4.

private String unconvert(int i)
{ String extra_digit = "";
  int cents = i % 100;
  if ( cents < 10 )
     { extra_digit = "0"; }
  return ((i / 100) + "." + extra_digit + cents);
}

Multiple Objects from the Same Class

public class AccountManager2
{ ...
  public void processTransactions()
  { ...
        else if ( command.equals("TPS") )
         { int amount = reader.readAmount();
           boolean ok = primary_account.withdraw(amount);
           if ( ok )
                { secondary_account.deposit(amount);
                  primary_writer.showTransaction("Transfer of $", amount);
                  secondary_writer.showTransaction("Deposit of $", amount);
                }
           else { primary_writer.showTransaction("transfer invalid", amount); }
         }
   ...
  }
}

The Logic of the Conditional Statement

1.1

!(x < 0 || x > 59)
is equivalent to  !(x < 0) && !(x > 59)
is equivalent to  x >= 0  &&  x <= 50

1.2.

!(!(x == 0) && y !- 1)
is equivalent to  !!(x == 0) || !(y != 1)
is equivalent to  x == 0 || y == 1

1.3.

!(x > 0) && !(y != 1 || x >= 0)
is equivalent to   x <= 0  &&  (!(y != 1)  && !(x >=0))
is equivalent to   x <= 0  &&  !(y != 1)  && !(x >=0)
is equivalent to   x <= 0  &&  y == 1  &&  x < 0
is equivalent to   y == 1  &&  x < 0

1.4.

!(x == 0 && (x != x) && true) 
is equivalent to   !(x == 0)  ||  !(x != x)  ||  !true
is equivalent to   x != 0  ||  x == x  ||  false
is equivalent to   x != 0  ||  true  ||  false
is equivalent to   true

2.1. One possible TEST is c.increment() && c.equalsZero(), which computes to false; in contrast, c.equalsZero() && c.increment() computes to true. The reason for the diffent results is the assignment to the private field inside increment.

2.2. The example !(c.increment()) || c.equalsZero() operates similarly, and note that c.equalsZero() || !(c.increment()) does not increment the private field.

2.3. Consider false && c.increment(): This expression does not increment the private field, whereas a version of conjunction that forces both operands to compute to answers would indeed increment the private field.

3.1

public int max(int x, int y, int z)
{ int big;
  // goal is to assign maximum of x, y, z  to  big:

  if ( x >= y  &&  x >= z )
       // assume  x >= y  &&  x >= z
       // that is,  x  is the maximum
       { big = x; }
       // goal achieved

  else // assume !(x >= y  &&  x >= z)
       // that is,  x < y  ||  x < z
       // therefore,  x  is not maximum, so it is  y  or  z:
       { if ( y >= z ) 
              // assume  y >=  z
              // therefore,  y  is maximum
              { big = y; }
              // goal achieved

         else // assume  !(y >= z)
              // that is,  y < z
              // therefore,  z  is maximum
             { big = z; }
             // goal achieved
       }
  // in all cases, the goal is achieved
  return big;
}

3.2

public int max(int x, int y, int z)
{ int big = x;
  // goal is to assign maximum of x, y, z  to  big:
  // so far,  big  holds the maximum of this set:  {x}

  // goal of this conditional is to set  big  to hold maximum of  {x, y}:
  if ( y > big )
     // assume y > big
     // therefore,  y  is maximum of set  {x, y}
     { big = y; }
     // goal achieved
  // else  !(y > big),  that is,  y <= big,  and  big  holds maximum of {x, y};
  //   goal is still achieved

  // so far,  big  holds the maximum of this set:  {x, y} 
  // goal of this conditional is to set  big  to hold maximum of  {x, y, z}:
  if ( z > big )
     // assume  z > big
     // therefore,  z  is maximum of set  {x, y, z}
     { big = z; }
     // goal achieved
  // else  !(z > big),  that is,  z <= big,  and  big  holds
  //  maximum of {x, y};   goal is still achieved

  // goal achieved
  return big;
}

Chapter 7: Patterns of Repetition: Iteration and Recursion

While-Statements

1.1

a
aa
aaa
aaaa

1.2. This prints the integers from 10 to 1.

1.3.

-1 4
0 3
1 2

1.4. This prints nothing.

1.5

a
aaa
aaaaa 

2. Here is one of many possible solutions:

char c = 'a';
while ( c != 'z' )
      { System.out.println(c);
        c = c + 1;
      }

3. Again, there are many possible solutions; one goes

int count = 2;
while ( count <= 50 )
      { if ( 1000 % count == 0 )
           { System.out.println(i); }
             count = count + 1;
      }

Definite Iteration

1.1

import javax.swing.*;
/** ExamStatistics contains methods for calculating exam statistics */
public class ExamStatistics
{ public double computeAverage(int how_many)
  { ... }

 public static void main(String[] args)
 { ExamStatistics calc = new ExamStatistics();
   String s = JOptionPane.showInputDialog("Type quantity of exams:");
   int quantity = new Integer(s).intValue();
   double average = calc.computeAverage(quantity); 
   JOptionPane.showMessageDialog(null, "Average score is " + average);
  }
}

1.2

public double computeAverage(int how_many)
{ double answer = 0.0;
  if ( how_many < 1 )
       { JOptionPane.showMessageDialog(null,
             "ExamStatistics error: exam quantity nonpositive");
       }
  else { double total_points = 0.0;  // the total of all test scores
         int count = 0;  // the quantity of tests read so far
         while ( count != how_many )
               { ...  the loop operates the same as before ... }
         // at conclusion: total_points == exam_1 + exam_2 + ... + exam_how_many
         answer = total_points / how_many;
       }
  return answer;
}

1.3.

public double computeBetterAverage(int how_many)
{ double answer = 0.0;
  if ( how_many < 1 )
       { JOptionPane.showMessageDialog(null,
             "ExamStatistics error: exam quantity nonpositive");
       }
  else { double total_points = 0.0;  // the total of all test scores
         int low_score = 0;  // the lowest score read so far
         int count = 0;  // the quantity of tests read so far
         while ( count != how_many )
               { String input = JOptionPane.showInputDialog
                                   ("Type next exam score:");
                 int score = new Integer(input).intValue();
                 total_points = total_points + score;
                 // check if  score  is the new lowest score:
                 if ( score < low_score  ||  count == 0 )
                    { low_score = score; }
                 count = count + 1;
               }
          answer = (total_points - low_score) / (how_many - 1);
        }
  return answer;
}

1.4.

import javax.swing.*;
public class ExamStatistics
{ public int computeMaxScore(int how_many)
  { int max_score = 0;
    if ( how_many < 1 )
         { JOptionPane.showMessageDialog(null,
               "ExamStatistics error: exam quantity nonpositive");
         }
    else { int count = 0;  // the quantity of tests read so far
           while ( count != how_many )
                  // at each iteration: max_score  holds the maximum of
                  //   exam_1, exam_2, ..., exam_count  read so far:
                 { String input = JOptionPane.showInputDialog
                                       ("Type next exam score:");
                   int score = new Integer(input).intValue();
                   if ( score > max_score )
                      { max_score = score; }
                   count = count + 1;
          }
         }
    return max_score;
  }

  public static void main(String[] args)
  { ExamStatistics calc = new ExamStatistics();
    String s = JOptionPane.showInputDialog("Type quantity of exams:");
    int quantity = new Integer(s).intValue();
    int max = calc.computeMaxScore(quantity);
    JOptionPane.showMessageDialog(null, "Highest score is " + max);
  }
}
2.
>int t = 4; int count = 2; while ( count <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 4 |
   >int count = 2; while ( count <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 4 |  int count ==| 2 |
   >while ( count <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 4 |  int count ==| 2 |
   >while ( 2 <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 4 |  int count ==| 2 |
   >while ( true ) { t = t * 2; count = count+1; }

=> int t ==| 4 |  int count ==| 2 |
   while ( true ) { >t = t * 2; count = count+1; }

=> int t ==| 4 |  int count ==| 2 |
   while ( true ) { >t = 8; count = count+1; }

=> int t ==| 8 |  int count ==| 2 |
   while ( true ) { ... >count = count+1; }

=> int t ==| 8 |  int count ==| 3 |
   while ( true ) { ... > }
At this point, the loop repeats:
=> int t ==| 8 |  int count ==| 3 |
   >while ( count <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 8 |  int count ==| 3 |
   >while ( true ) { t = t * 2; count = count+1; }

=> int t ==| 8 |  int count ==| 3 |
   while ( true ) { >t = t * 2; count = count+1; }
Two steps later, we have the following:
=> int t ==| 16 |  int count ==| 4 |
   while ( true ) { ... > }

=> int t ==| 16 |  int count ==| 4 |
   >while ( count <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 16 |  int count ==| 4 |
   >while ( true ) { t = t * 2; count = count+1; }

=> int t ==| 16 |  int count ==| 4 |
   while ( true ) { >t = t * 2; count = count+1; }

=> int t ==| 32 |  int count ==| 5 |
   while ( true ) { ... > }

=> int t ==| 32 |  int count ==| 5 |
   >while ( count <= 4 ) { t = t * 2; count = count+1; }

=> int t ==| 32 |  int count ==| 5 |
   >while ( false ) { t = t * 2; count = count+1; }

=> int t ==| 32 |  int count  ==| 5 |
    ... >

3.

int count = 8;
while ( count >= 1 )
      // so far, have printed:  88, 77 ...downto... (i+1)*11
      { System.out.print(i*11 + " ");
        count = count - 1; 
      }
System.out.println();

4.

import javax.swing.*;
public class Test4
{ public static void main(String[] args)
  { int stopping_point = 4;
    int count = 0;
    String sentence = "";
    while ( count != stopping_point)
          { String input = JOptionPane.showInputDialog ("Type a word:");
            sentence = input + " " + sentence;
            count = count + 1;
          }
    JOptionPane.showMessageDialog(null, sentence);
  }
}

5.1.

public int summation(int n)
{ int total = 0;  // the running total
  int count = 0;  // counts up to n
  while ( count != n )
        // at each iteration:  total == 0+1+...up to...+count
        { count = count + 1;
          total = total + count;
        }
        // total == 0+1+...up to...+n
  return total;
}

5.2

/** product  computes the iterated product  a*(a+1)*(a+2)*...*b
  * @param  a - the starting int for the product
  * @param  b - the ending int for the product
  * @return  a*(a+1)*(a+2)*...*b, if  a <= b;
  *  return 1,  if  a > b   */
public int product(int a, int b)
{ int total = 1;
  if ( a <= b )
     { total = a; 
       int count = a;
       while ( count != b )
             // invariant: total == a * (a+1) * ...up to... * i
             { count = count + 1;
               total = total * count;
             }
       // total == a * (a+1) * (a+2) * ... * b == product(a, b)
     }
  return total;
}

5.3.

/** factorial  computes  n!  for  n  in the range 0..20.
  * (note: values for n>20 are too large to compute)
  * @param  n - should be in the range 0..20
  * @return n!, if  n  in 0..20; returns -1 otherwise  */
public long factorial(int n)
{ long fac = -1;
  if ( n >= 0  &&  n <= 20 )
     { int count = 0;  fac = 1;
       while ( count != n )
             // invariant: fac == 1*2*...up to...*count
             { count = count + 1;
               fac = fac * count;
             }
       // fac == factorial(n)
     }
  return fac;
}
5.4.
public double sine(double x)
{ double sin = x;
  int count = 3;
  while ( count <= 19 )
        // invariant:  sin == x - (x^3/3!) + ...up to... (x^(count-2)/(count-2)!)
        { double d = Math.pow(x, count) / factorial(count); 
          if ( (count % 4) == 3 )
               { sin = sin - d; }
          else { sin = sin + d; }
          count = count + 2;
        }
  return sin;
}

Definite-Iteration Example: Painting a Bulls-Eye

1. Within paintBullsEye, the statement, int ring_width = size / rings, computes the width of each ring. Since size is 200 and rings is 21, the expression, size / rings computes to 9---the fraction is discarded. The method therefore paints each circle to be 9 pixels smaller than its predecessor: 200 pixels diameter, 191, 182, and so on. This makes the last circle to have diameter 200 - (20 * 9) = 18 pixels---the fractions of pixels add up to 9 extra pixels.

Nontermination

1.

public class Test
public static void main(String[] args)
{ int denominator = 2;
  while ( true )  // a simple way to make a loop execute forever
        { double fraction = 1.0 / denominator;
          JOptionPane.showMessageDialog(null,
                          "1/" + denominator + " = " + fraction);
          denominator = denominator + 1;
        }
}

Indefinite Iteration: Input Processing

1. It sums a sequence of nonnegative integers and terminates when a negative number is input.

2. The solution is identical to that of Exercise 1.1 of the Definite Iteration section.

3.

public static void main(String[] args)
{ String text = "";
  boolean processing = true;
  while ( processing )
        { String input = JOptionPane.showInputDialog("Type some text:");
          if ( input == null         // was Cancel pressed?
             || input.equals("") )   // was an empty line Entered?
               { processing = false; }
          else { text = text + input + "\n"; }
        }
  System.out.println(text);
}

4. The processTransactions method now reads:

  /** processTransactions  processes user commands until a Q is entered */
  public void processTransactions()
  { boolean processing = true;
    while ( processing )
          // so far, all bank transactions have been correctly processed
          { char command = reader.readCommand("Command (D,W,Q) and amount:");
            if ( command == 'Q' )  // the signal to stop
                 { processing = false; }
            else if ( command == 'D' )  // deposit?
                      { ... see Figure 12, Chapter 6 ... }
                   else if ( command == 'W' )  // withdraw?
                      { ... see Figure 12, Chapter 6 ... }
                   else { writer.showTransaction("Illegal command"); }
                 }
          }
  }
}

Indefinite Iteration: Searching

1. The statements that are altered are marked by (*):

public int findChar(char c, String s)
{ boolean found = false;
  int index = s.length() - 1;  // (*)
  while ( !found  &&  index != -1 )  // (*)
        // invariant:
        // (*) (1) found == false  means
        //      c  is not any of chars  length(s)-1...downto...(index+1)  in s
        // (2) found == true  means that  c  is  s.charAt(index)
        { if ( s.charAt(index) == c )
               { found = true; }
          else { index = index - 1; }  // (*)
        }
  return index;
}

2.

/** powerOfTwo determines whether its argument is a power of 2.
  * @param n - the argument, a nonnegative int
  * @return m, such that n == 2^m,  if  n  is a power of 2
  *  return -1 if  n  is not a power of 2 */
public int powerOfTwo(int n)
{ int answer;
  if ( n <= 0 )
       { System.out.println("powerOfTwo error: negative arg " + n );
         answer = -2;   // return a clearly bad result
       }
  else { // search for a factor that is not divisible by 2:
         boolean item_found = false;
         int current = n;
         int power = 0;
         while ( !item_found  &&  current != 1 )
               // invariant:   n == current * (2^power),  and also
               //  item_found == true implies  current%2 != 0
               { if ( current%2 != 0 )
                      { item_found = true; }
                 else { power = power + 1;
                        current = current/2;
                      }
               }
        if ( item_found )
             { answer = -1; }
        else { answer = power; }  // because !item_found implies current==1
      }
  return answer;
}

3.1.

/** Primes prints all primes from 2 up to 100. */
public class Primes
{
  public static void main(String[] args)
  { int i = 2;
    while ( i != 101 )
          // invariant: all primes in range 2 up to (i-1) have been printed
          { if ( isPrime(i) == 0 )
               { System.out.println( i + " is a prime."); }
            i = i+1; }
  }

  private static int isPrime(int n)
  { ... }  // see Figure 3, Chapter 7
}

3.2.

/** Primes2 prints which input numbers are primes.
  * Input: a sequence of integers, terminated by one < 2
  * Output: whether or not each input integer is prime.  */
public class Primes2
{
  public static void main(String[] args)
  { boolean processing = true;
    while ( processing )
          // invariant: all user inputs have been correctly decided as primes
          { int i = new Integer
               (JOptionPane.showInputDialog("Type an int:")).intValue();
            if ( i < 2 )
                 { processing = false; }
            else { if ( isPrime(i) == 0 )
                        { System.out.println( "is a prime"); }
                   else { System.out.println("is not a prime"); }
                 }
          }
  }

  private static int isPrime(int n)
  { ... }  // see Figure 3, Chapter 7
}

For-Statements

1.

public double computeAverage(int how_many)
{ double total_points = 0.0;  // the total of all test scores
  int count = 0;  // the quantity of tests read so far
  for ( int count = 0;  count != how_many;  count = count + 1 )
      // at each iteration: total_points == exam_1 + exam_2 + ... + exam_count
      { String input = JOptionPane.showInputDialog("Type next exam score:");
        int score = new Integer(input).intValue();
        total_points = total_points + score;
        System.out.println("count = " + count + "; total = " + total_points);
      }
  return (total_points / how_many);
}

2.

  /** reverse reverses a string argument
    * @param s - the string to be reserved
    * @return the reversed string, e.g., return "cba" when  s  is  "abc" */
  public String reverse(String s)
  { String answer = "";
    for ( int i = 0;  i != s.length();  i = i+1 )
        // invariant: answer  holds first i characters of  s  in reverse order
        { answer = s.charAt(i) + answer; }
    return answer;
  }

Nested Loops

1. We merely change the termination conditions of the two loops:

for ( int i = 0; i <= 5; i = i + 1 )
    { // invariant: printed  0*x  up to  (i-1)*x,  for all values  x  in 0..4
      for ( int j = 0; j <= 5; j = j + 1 )
          // invariant: printed  i*0  up to  i*(j-1)
          { System.out.print(i + "*" + j + "=" + (i * j) + " "); }
      System.out.println();
    }

2. The nested loop is placed in a helper method, notFoundIn:

/** removeDuplicateLetters  constructs a string that contains the
  *  same letters as its argument except that all duplicate letters
  *  are removed, e.g.,  for argument, "butterflies", the result is
  *  "buterflis"
  * @param s - the argument string
  * @return a string that looks like  s  but with no duplicates  */
public String removeDuplicateLetters(String s)
{ String answer = "";
  for ( int i = 0;  i != s.length();  i = i + 1 )
       // invariant:  answer  holds exactly one instance of every char
       //  that appears in the substring,  s.charAt(0) ..upto.. s.charAt(i-1)
       { if ( notFoundIn(answer, s.charAt(i)) )
            { answer = answer + s.charAt(i); }
       }
  return answer;
}

/** notFoundIn  returns  true  exactly when  c  does not appear within  a */
private boolean notFoundIn(String a, char c)
{ boolean notfound = true;
  for ( int j = 0;  j != a.length();  j = j + 1 )
      // invariant:  notfound  states whether  c  is absent from substring,
      //   a.charAt(0) ..upto.. a.charAt(j-1)
      { notfound = notfound  &&  (c != a.charAt(j)); }
  return notfound;
}

3.

for ( int i = 0; i <= 3; i = i + 1 )
      for ( int j = 0; j <= i; j = j + 1 )
          { System.out.print(i + " " + j); }
      System.out.println();
    }

Case Study: Bouncing Ball Animation

1. The move method within class MovingBall moves the ball first and checks the ball's position second. Thus, it is possible for the ball to intersect the walls of the box. To prevent this, move must check the ball's position before it moves the ball and if necessary move the ball just to the exact location of the wall and no further.

2. To create an animation with two balls, create two MovingBall objects and insert them into the box. Here are the classes that receive the simple changes:

import java.awt.*;
/** BounceTheBall constructs and starts the objects in the animation.  */
public class BounceTheBall2
{ public static void main(String[] args)
  { // construct the model objects:
    int box_size = 200;
    int balls_radius = 6;
    Box box = new Box(box_size);
    MovingBall ball1 = new MovingBall((int)(box_size / 2.0),
                                     (int)(box_size / 5.0),
                                     balls_radius, box);
    MovingBall ball2 = new MovingBall((int)(box_size / 5.0),
                                     (int)(box_size / 2.0),
                                     balls_radius, box);
    // construct the output-view objects:
    BallWriter ball1_writer = new BallWriter(ball1, Color.red);
    BoxWriter box_writer  = new BoxWriter(box);
    BallWriter ball2_writer = new BallWriter(ball2, Color.blue);
    AnimationWriter2 writer
      = new AnimationWriter2(box_writer, ball1_writer, ball2_writer, box_size);
    // construct the controller and start it:
    new BounceController2(ball1, ball2, writer).runAnimation();
  }
}


import java.awt.*;
import javax.swing.*;
/** AnimationWriter2 displays a box with two balls in it.  */
public class AnimationWriter2 extends JPanel
{ private BoxWriter box_writer;    // the output-view of the box
  private BallWriter ball1_writer;  // the output-view of the ball in the box
  private BallWriter ball2_writer;  // the output-view of the ball in the box

  /** Constructor AnimationWriter construct the frame
    * @param b - the box's writer
    * @param l1 - the ball's writer
    * @param l2 - the ball's writer
    * @param size - the frame's size */
  public AnimationWriter2(BoxWriter b, BallWriter l1, BallWriter l2, int size)
  { box_writer = b;
    ball1_writer = l1;
    ball2_writer = l2;
    JFrame my_frame = new JFrame();
    my_frame.getContentPane().add(this);
    my_frame.setTitle("Bounce");
    my_frame.setSize(size, size);
    my_frame.setVisible(true);
  }

  /** paintComponent paints the box and ball
    * @param g - the graphics pen */
  public void paintComponent(Graphics g)
  { box_writer.paint(g);
    ball1_writer.paint(g);
    ball2_writer.paint(g);
  }
}


/** BounceController controls two moving balls.  */
public class BounceController2
{ private MovingBall ball1;  // model object
  private MovingBall ball2;  // model object
  private AnimationWriter2 writer; // output-view object

  /** Constructor BounceController initializes the controller
    * @param b1 - a ball
    * @param b2 - another ball
    * @param w - the output-view object  */
  public BounceController2(MovingBall b1, MovingBall b2, AnimationWriter2 w)
  { ball1 = b1;
    ball2 = b2;
    writer = w;
  }

  /** runAnimation  runs the animation by means of an internal clock */
  public void runAnimation()
  { while ( true )
          { delay(20);   // delay 20 milliseconds --- one clock ``tick''
            ball1.move();
            ball2.move();
            writer.repaint();  // redisplay box and ball
          }
  }

  /** delay pauses execution for  how_long  milliseconds */
  private void delay(int how_long)
  { try { Thread.sleep(how_long); }
    catch (InterruptedException e) { }
  }
}

Counting with Multiple Recursions

1.

public class TestFactorial
{
  public static void main(String[] args)
  { for ( int i = 3;  i <= 20;  i = i+3 )
    // invariant: have printed the factorials for  3 ..upto.. (i-1)
    { System.out.println( i + "! = " +  factorial(i) ); }
  }

  public static long factorial(int n)
  { ... }
}

2. You will obtain this output:

20! = 2432902008176640000
21! = -4249290049419214848
99! = 0
Exception in thread "main" java.lang.StackOverflowError
        at TestFactorial.factorial(Compiled Code)
        at TestFactorial.factorial(Compiled Code)
         ...
The answer for 21! is clearly incorrect and arises because long variables hold numbers of about 20 digits maximum. When a larger number is inserted, overflow occurs and some of the number's significant digits are truncated. (In the above example, the mutilated answer looks like a negative.) An even more extreme example of this phenomenon occurs at 99! Finally, the attempt to compute -1! causes an unbounded quantity of recursive invocations, which completely fill computer storage with uncompleted copies of factorial. This causes a StackOverflowError.

3. See the solution to Exercise 5 in the section, ``Definite Iteration.''

4.

/** fibonacci  computes the fibonacci function */
public static int fibonacci(int n)
{ int answer;
  if ( n == 0  ||  n == 1 )
       { answer = 1; }
  else { answer = fibonacci(n-1) + fibonacci(n-2); }
  System.out.println("fibonacci(" + n + ") = " + answer);
  return answer;
}
We can calculate that fibonacci(20) is 10946, fibonacci(30) is 1346269, fibonacci(35) is 14930352, and fibonacci(40) is 165580141. These are not huge integers, but the computation goes slowly because the function is constantly recomputing the same answers. (The System.out.println statement in the method will display this clearly!) For example, fibonacci(20) sends messages to compute fibonacci(19) and fibonacci(18), and fibonacci(19) sends messages to compute fibonacci(18) and fibonacci(17), even though fibonacci(18) is already being computed. These recomputations explode as the actual parameters decrease in value.

In Chapter 8 we learn how to use arrays to avoid the redundant computations.

5.

/** product  computes the product  a*(a+1)*(a+2)*...*b
  * @param  a - the starting int for the product
  * @param  b - the ending int for the product
  * @return  a*(a+1)*(a+2)*...*b, if  a <= b;
  *  return 1,  if  a > b   */
public int product(int a, int b)
{ int total;
  if ( b < a )
       { total = 1; }
  else { total = product(a, b-1) * b; }
  return total;
}
Compare this solution to that of Exercise 5 in the section, ``Definite Iteration.''

6.

/** add computes  a + b, for nonnegatives  a and b  */
public int add(int a, int b)
{ int result;
  if ( a == 0 )
       { result = b; }
  else { result = add(a-1, b) + 1; }
  return result;
}

/** mult computes  a * b, for nonnegatives  a and b  */
public int mult(int a, int b)
{ int result;
  if ( a == 0 )
       { result = 0; }
  else { result = add(mult(a-1, b), b); }
  return result;
}

/** exp computes  a^b, for nonnegatives  a and b  */
public int exp(int a, int b)
{ int result;
  if ( b == 0 )
       { result = 1; }
  else { result = mult(a, exp(a, b-1)); }
  return result;
}

7.

public static int ackermann(int m, int n)
{ int result;
  if ( m == 0 )
       { result = n+1; }
  else if ( n == 0 )
       { result = ackermann(m-1, 1); }
  else { result = ackermann(m-1, ackermann(m, n-1)); }
  System.out.println("ackermann(" + m + ", " + n + ") = " + result);
  return result;
}
Try this method on small examples first, e.g., ackermann(2, 2). Then, use your watch to time how long your computer takes to compute ackermann(3, 5).

8.

number_of_sorted_words(1) = 1
One letter by itself is a sorted word.
number_of_sorted_words(n) = q + 1 + q,
  where  q = number_of_sorted_words(n-1)
Here is the justification: Say that we have written q distinct, sorted words with n-1 distinct letters, and say that 'z' is a new letter that is distinct from all the letters used so far. The sorted words we can write now includes (i) the ones we already wrote; (ii) 'z' by itself; (iii) all the new sorted words we can build from the old sorted words plus 'z'. In the last case, there are exactly q such sorted words, which are constructed by inserting 'z' into its proper position within each previously sorted word.

9. The flies produced by one ordinary fly after n-generations are

flies_from_ordinary(0) = 1
flies_from_ordinary(n) = flies_from_ordinary(n-1) + flies_from_queen(n-1) + 1
The flies produced by one queen fly after n-generations are
flies_from_queen(0) = 1
flies_from_queen(n) = flies_from_ordinary(n-1) + flies_from_ordinary(n-1)
                      + flies_from_queen(n-1)
The second equation makes sense if you pretend that a queen lays eggs for two ordinary flies, immediately dies, and is reborn as a new queen that can lay two eggs. (Of course, this is the same as the queen laying two eggs for every generation.)

Drawing Recursive Pictures

1. Revise the paintPicture method:

  private void paintPicture(int right_border, int bottom, int egg_size,
                            Graphics g)
  { int background_egg_size = (int)(egg_size * SCALE);
    if ( background_egg_size > 0 )  // is the background worth painting?
       { paintPicture((int)(right_border * SCALE), (int)(bottom * SCALE),
                      background_egg_size, g);
       }
    // paint an egg in the foreground:
    paintAnEgg(0, bottom, egg_size, g);
  }

2.

import java.awt.*;
import javax.swing.*;
/** CirclesWriter draws a collections of concentric circles  */
public class CirclesWriter extends JFrame
{ private double SCALE = 0.8;  // the size of the background picture relative
                               //   to the foreground
  private int WIDTH = 300;
  private int DIAMETER = 200;

  /** Constructor RecursivePictureWriter creates the window */
  public CirclesWriter()
    JFrame my_frame = new JFrame();
    my_frame.getContentPane().add(this);
    my_frame.setTitle("Circles");
    my_frame.setSize(WIDTH, WIDTH);
    my_frame.setBackground(Color.white);
    my_frame.setVisible(true);
  }

  /** paintComponent paints the circles
    * @param g - the ``graphics pen'' */
  public void paintComponent(Graphics g)
  { drawCircles(DIAMETER, g); }

  private void drawCircles(int size, Graphics g)
  { if ( size > 0 )
       { int corner_position = (WIDTH / 2) - (size / 2);
         g.drawOval(corner_position, corner_position, size, size);
         drawCircles((int)(size * 0.8), g);
       }
  }
}

4. The number of eggs that appear in an n-layer tree is defined as

numberEggs(0) = 0
numberEggs(n) = 1 + numberEggs(n-1) + numberEggs(n-1),  for positive  n

Loop Invariants

1. An invariant is answer = a * (b-i).

This equation is true when the loop is entered the first time, because b equals i, hence answer = a * 0.

Now, after some iterations, assume that answer_start = a * (b - i_start) holds true, and say that the loop is repeated one more time. The loop's body changes the value of variable answer to answer_next == answer_start + a. Also, we have that i_next == i_start - 1. Does answer_next = a * (b - i_next)? We already know that the equation answer_start = a * (b - i_start) holds true; we add a to both sides of the equation: answer_start + a = (a * (b - i_start)) + a. A bit of easy algebra lets us rewrite this true equation into answer_start + a = a * (b - i_start + 1), which implies answer_start + a = a * (b - (i_start - 1)). But we can substitute for answer_start + a and i_start - 1, giving us: answer_next = a * (b - i_next), showing that the invariant still holds.

2. An invariant is n = (q * d) + rem.

The argument that the invariant holds true follows the form of the solution of the previous exercise: on loop entry, it is easy to verify that n = (0 * d) + n.

Now, after some iterations, assume that the invariant is still holding true, and say that one more repetition of the loop body is performed. We have that q_next = q_start + 1; rem_next = rem_start - d. We do the following algebra on the invariant: n = (q_start * d) + rem_start, therefore, n = (q_start * d) + d + rem_start - d, therefore, n = (q_next * (d + 1)) + rem_next, therefore, n = (q_next * d) + rem_next, which shows that the invariant remains true.

3. An invariant is total = n^i. The argument of invariance proceeds as above.

4. The invariant is in two parts: (1) item_found == false implies 'A' is not present in s.substring(0, index); (2) item_found == true implies s.charAt(index)=='A'.

The invariant holds on loop entry, because item_found is false and s.substring(0, 0) is the empty string. The invariant is preserved by consideration of the conditional:

5. Let reverse(w) denote the string w spelled backwards. An invariant is t = reverse(s.substring(0, i)).

The invariant holds true at loop entry, because s.substring(0, 0) equals the empty string.

After some iterations, assume that the invariant is true. We have that t_next = s.charAt(i_start) + t_start and i_next = i_start + 1. We do the following algebra on the invariant: t_start = reverse(s.substring(0, i_start)), therefore s.charAt(i_start) + t = s.charAt(i_start) + reverse(s.substring(0,i_start)), therefore, s.charAt(i_start) + t = reverse( s.substring(0, i_start) + s.charAt(i_start)). But s.substring(0, i_start) + s.charAt(i_start) is the same as s.substring(0, i_start + 1), which is s.substring(0, i_next). Hence, we have t_next = reverse(s.substring(0, i_next)).

Loop Termination

1. The multiplication program terminates when b is nonnegative; in that case, the termination expression (``alarm clock'') is just i.

2. Thanks to the enclosing conditional, the division program terminates in all cases; the termination expression is rem - d.

3. The exponentiation program always terminates; the alarm is p - i.

4. The character-search program always terminates; the alarm is s.length() - index .

5. The program always terminates; the termination expression is s.length() - i .

Chapter 8: Arrays

Why We Need Arrays

1.1 In Java, a newly constructed array contains zeros:

0
0
0
0

1.2.

0
10
0
12

1.3.

0
2
4
6

1.4.

10
20
40
80

2.

integer[] powers_of_two = new integer[10];
powers_of_two[0] = 1;
for ( int i = 1; i != 10; i = i + 1 )
    { powers_of_two[i] = powers_of_two[i - 1] * 2; }

3.

char[] letter = new char[26];
for ( int i = 0; i != 26; i = i + 1 )
    { letter[i] = 'a' + i; } // Java lets us add an int to a char to make a char
for ( int i = 25; i >= 0; i = i - 1 )
    { System.out.print(letter[i]); }
System.out.println();

4.

double[] reciprocals = new double[10];
for ( int i = 1; i != 10; i = i + 1 )
    { reciprocals[i] = 1.0 / i; }

Collecting Input Data within Arrays

1. The additions to the program are marked by (*):

public class VoteCount2
{ public static void main(String[] args)
  { int num_candidates = 4;                    // how many candidates
    int[] votes = new int[num_candidates];     // holds the votes
    int vote_count = 0;  // (*) remembers quantity of votes cast

    boolean processing = true;
    while ( processing )
          // all votes read so far have been tallied in  votes
          { int v = new Integer(JOptionPane.showInputDialog
                      ("Vote for (0,1,2,3): ").intValue();
            if ( v == -1 )
                 { processing = false; }
            else if ( v >= 0  &&  v < num_candidates )
                 { votes[v] = votes[v] + 1;
                   vote_count = vote_count + 1; // (*)
                 }
            else { ... }
          }
    System.out.println("Total votes cast = " + vote_count); // (*)
    ...
    int winner = 0; // (*) remembers the winner of the election
    boolean there_is_a_tie = false;  // (*) remembers if the election is a tie
    for ( int i = 1;  i != num_candidates;  i = i+1 ) // (*)
        { if ( votes[i] > votes[winner] ) 
             { winner = i;
               there_is_a_tie = false;
             }
          else if ( votes[i] == votes[winner] )
             { there_is_a_tie = true; }
        }
    if ( there_is_a_tie )  // (*)
         { System.out.println("The election is a tie!"); }
    else { System.out.println("The winner is Candidate " + winner); }
  }
}

2. The key alteration is replacing the statement, int num_candidates = 4, by

int num_candidates = new Integer(JOptionPane.showInputDialog
       ("Enter quantity of candidates (a nonnegative int): ").intValue();

3. At the beginning of the program, insert these statements, which read the candidates' names:

String[] name = new String[num_candidates]; // (*) holds candidates' names
for ( int i = 0;  i != num_candidates;  i = i+1 )
    { name[i] = JOptionPane.showInputDialog("Type name of Candidate " + i); }
At the end of the program, alter the loop that prints the candidates' totals to read as follows:
for ( int i = 0;  i != num_candidates;  i = i + 1 )
    { System.out.println("Candidate " + name[i] + " has "
                          + votes[i] + " votes");
    }

4. The solution to this exercise is essentially the same as the solution to Exercise 1.

Translation Tables

1.

public class Translate
{ public static void main(String[] args)
  { int[] code = new int[27];   // this is the translation table:
                            // code[0] holds the code for ' ',
                            // code[1] holds the code for 'a',
                            // code[2] holds the code for 'b', and so on
    int k = new Integer(JOptionPane.showInputDialog
               ("Type seed (an integer): ").intValue();
    code[0] = k;
    for ( int i = 1;  i != code.length;  i = i+1 )
        { code[i] = (code[i-1] * 2) + 1; }

    String input_line = JOptionPane.showInputDialog("Type sentence to encode:");
    for ( int j = 0;  j != input_line.length();  j = j+1)
        { char c = input_line.charAt(j);
          if ( c == ' ' )
               { System.out.println(code[0]); }
          else if ( c >= 'a'  &&  c <= 'z' )
               { int index = (c - 'a') + 1;
                 System.out.println(code[index]);
               }
          else { System.out.println("error: bad input character"); }
        }
  }
}

2.

public class Decode
{ public static void main(String[] args)
  { int[] code = new int[27];   // this is the translation table:
                            // code[0] holds the code for ' ',
                            // code[1] holds the code for 'a',
                            // code[2] holds the code for 'b', and so on
    int k = new Integer(JOptionPane.showInputDialog
             ("Type seed (an integer): ").intValue();
    code[0] = k;
    for ( int i = 1;  i != code.length;  i = i+1 )
        { code[i] = (int)((code[i-1] * 1.3) + 1); }

    String answer = "";
    boolean processing = true;
    while ( processing )
          {  String input = JOptionPane.showInputDialog
                       ("Type an integer to decode (or nothing, to quit): ");
             if ( input.equals("") )
                  { processing = false; }
             else { char c = decode(code, new Integer(input).intValue());
                    answer = answer + c;
                  }
          }
    System.out.println(answer);
  }

  /** decode  translates an integer code into a character
    * @param code - the array holding the codes for  ' ', 'a', 'b', ..., 'z'
    * @param i - the integer to be decoded
    * @return the matching character, or '*' if the integer does not decode */
  private static char decode(int[] code, int i)
  { char c = '*';
    boolean found = false;
    int index = 0;
    while ( !found  &&  index != code.length )
          { if ( code[index] == i )
                 { found = true;
                   if ( index == 0 )
                        { c = ' '; }
                   else { c = (char)(index + 'a' - 1); }
                 }
            else { index = index + 1; }
          }
    return c;
  }
}

3.

integer[] powers_of_two = new integer[10];
powers_of_two[0] = 1;
for ( int i = 1; i != 10; i = i + 1 )
    { powers_of_two[i] = powers_of_two[i - 1] * 2; }

4.1.

int[] factorial = new int[20];
factorial[0] = 1;
for ( int n = 1;  n != factorial.length;  n = n + 1 )
    { factorial[n] = n * factorial[n-1]; }

4.2

int[] fib = new int[20];
fib[0] = 1;
fib[1] = 1;
for ( int n = 2;  n != factorial.length;  n = n + 1 )
    { fib[n] = fib[n - 1] + fib[n - 2]; }
As noted in the solution to a similar exercise in Chapter 7, one recursive definition of the Fibonacci function performs many recursive invocations with the same actual parameter value. The loop computation computes the answer for each value exactly once.

Internal Structure of One-Dimensional Arrays

1.

int[] r = new int[15];
r[0] = 0;
for ( int i = 1;  i != r.length;  i = i+1 )
    { r[i] = r[i-1] + i; }

2.

double[] d = new double[30];
for ( int i = 0;  i != d.length;  i = i+1 )
    { d[i] = Math.sqrt(i); }
for ( int i = 0;  i != d.length;  i = i+1 )
    { System.out.println(i + " " + d[i]); }
Of course, the two loops can be combined:
for ( int i = 0;  i != d.length;  i = i+1 )
    { d[i] = Math.sqrt(i);
      System.out.println(i + " " + d[i]);
    }

3.

boolean[] b = new boolean[4];
b[0] = true;
b[1] = !b[0];
b[2] = b[0] && b[1];
for ( int i = 3;  i != 0;  i = i-1 )
    { System.out.println(b[i]); }

4.

  public int maxElement(int[] r)
  { int max = -1;
    if ( r.length == 0 )
         { System.out.println("maxElement error: array is empty"); }
    else { max = r[0];
           for ( int i = 1;  i != r.length;  i = i+1 )
               // invariant:  max  holds max. value in  r[0] ...upto... r[i-1]
               { if ( r[i] > max )
                    { max = r[i]; }
               }
         }
    return max;
  }

5.

  public double[] add (double[] r1, double[] r2)
  { double[] answer = null;
    if ( r1.length != r2.length )
         { System.out.println("add error: arrays incompatible"); }
    else { answer = new double[r1.length];
           for ( int i = 0;  i != answer.length; i = i+1 )
               { answer[i] = r1[i] + r2[i]; }
         }
    return answer;
  }

6.1.

BankAccount[] account = new BankAccount[100];
for ( int i = 0;  i != account.length;  i = i+1 )
    { account[i] = new BankAccount(0); }

6.2.

account[12].deposit(50);

int balance = account[12].balanceOf();
account[12].withdraw(balance);
account[45].deposit(balance);

6.3.

for ( int i = 0;  i != account.length;  i = i+1 )
    { int balance = account[i].balanceOf();
      if ( balance > 0 )
         { System.out.println("Account " + i + " has balance $" + balance); }
    }

6.4. account[12] = null;

6.5. The array element at index 15 holds the address of the same bank account object as the element at index 10---the account is shared (aliased) by both 10 and 15.

Arrays of Objects

1. Altered statements are marked by (*):

public class VoteCount3
{ public static void main(String[] args)
  { int num_candidates = 4;
    Counter[] votes = new Counter[num_candidates];     // (*) holds the votes
    // (*) create the Counter objects and place them into the array:
    for ( int i = 0;  i != num_candidates;  i = i+1 )  // (*)
        { votes[i] = new Counter(0); }                 // (*)
    boolean processing = true;
    while ( processing )
          { int v = new Integer(JOptionPane.showInputDialog
                     ("Vote for (0,1,2,3): ").intValue();
            if ( v >= 0  &&  v < votes.length )
                 { votes[v].increment(); }             // (*)
            else { processing = false; }
          }
    for ( int i = 0;  i != num_candidates;  i = i+1 )
        { System.out.println("Candidate" + i + " has "
                             + votes[i].getCount() + " votes");  // (*)
        }
  }
}
2.
  public BankAccount getAccount(int id_number)
  { BankAccount result = null;
    if ( id_number >= 0  &&  id_number < max_account )
       {  result = bank[id_number]; }
    return result;
  }

  public boolean deleteAccount(int id_number)
  { boolean result = false;
    if ( id_number >= 0  &&  id_number < max_account )
       { if ( bank[id_number] != null )
            { bank[id_number] = null;
              result = true;
            }
       }
    return result;
  }

Case Study: Databases

1. The changes are annoying but simple:

/** All occurrences of Record are replaced by  BankAccount1
    All occurrences of  Key  are replaced by  int
    In  locationOf,  replace  equals  by  ==  */
public class Database1
{ private BankAccount1[] base;   // the collection of records
   ...

  /** locationOf returns the index in  base  where a record with  k  appears*/
  private int locationOf(int k)
  { int result = -1;
     ...
          if ( base[i] != null  &&  base[i].keyOf() == k )
             { ... }
     ...
  }
}
Here is a testing program:
public class Test1
{
  public static void main(String[] args)
  { Database1 d = new Database1(5);

    BankAccount1 r1 = new BankAccount1(100, 1);
    System.out.println("Insert 1 is " + d.insert(r1));
    BankAccount1 r2 = new BankAccount1(200, 2);
    System.out.println("Insert 2 is " + d.insert(r2));

    System.out.println("Delete 2 is " + d.delete(2));

    BankAccount1 r = d.find(1);
    System.out.println("Find 1 is " + r.keyOf());
  }
}

2. Alter the solution to the previous exercise in this trivial way: Change all occurrences of BankAccount1 to BankAccount2.

3. Again, this solution is the same as the one to Exercise 1, where all occurrences of BankAccount1 become BankAccount3.

4.

/** All occurrences of Record are replaced by  VoteCounter
    All occurrences of  Key  are replaced by  String */
public class Database4
{ private VoteCounter[] base;   // the collection of records
   ...

  /** locationOf returns the index in  base  where a record with  k  appears*/
  private int locationOf(String k)
  { ...
        if ( base[i] != null  &&  base[i].keyOf().equals(k) )
                 { ... }
    ...
  }
}

5. Like the previous solution: all occurrences of Record are replaced by VoteCounter and all occurrences of Key are replaced by Person.

6. Add this method to class Person:

public boolean lessthan(Person other)
{ boolean answer = false;
  if ( my_name.compareTo(other.nameOf()) == -1 )  // my_name < other's name?
     { answer = true; }
  return answer;
}

Case Study: Playing Pieces for Card Games

1.

public class Test1
{
  public static void main(String[] args)
  {
    Card q = new Card(Card.HEARTS, Card.QUEEN);
    String suit = q.suitOf();
    int count = q.countOf();
    System.out.println("The card is the " + count + " of " + suit);
  }
}
Of course, this example displays The card is the 12 of hearts, which is inelegant. Perhaps the best solution is a translation method within class Card; add this method to it:
  /** toString displays the card's suit and count for printing
    * @return the string representation of the card */
  public String toString()
  { String s = " of " + suit;
    if ( count == ACE ) { s = "ace" + s; }
    else if ( count == JACK )  { s = "jack" + s; }
    else if ( count == QUEEN )  { s = "queen" + s; }
    else if ( count == KING )  { s = "king" + s; }
    else    { s = count + s; }
    return s;
  }
}
Then, the tester program can state, System.out.println("The card is the " + q.toString()).

2.

public class Test2
{
  public static void main(String[] args)
  { CardDeck deck = new CardDeck();
    for ( int i = 1;  i <= 53;  i = i+1 )
        { Card c = deck.newCard();
          System.out.println(c.countOf() + " of " + c.suitOf());
        }
  }
}

3.

public class Test3
{
  public static void main(String[] args)
  { CardDeck deck = new CardDeck();
    boolean processing = true;
    while( processing )
        { String request =
            JOptionPane.showInputDialog("Another card? (Or, stop):");
          if ( request.equals("stop") )
               { processing = false; }
          else { if ( !deck.moreCards() )
                      { System.out.println("Sorry---deck empty"); }
                 else { Card c = deck.newCard();
                        System.out.println(c.countOf() + " of " + c.suitOf());
                      }
               }
        }
  }
}

4.

public class Player
{ private Card[] hand;         // the cards held by this player
  private int card_count = 0;  // how many cards are held
    // invariant: hand[0]...hand[card_count-1] are exactly the cards held
  private int hand_size;       // max. count of cards that can be held

  /** Constructor Player initializes the object
    * @param size - the max. number of cards the player can hold */
  public Player(int size)
  { hand_size = size;
    hand = new Card[hand_size];
  }

  /** wantsACard asks the player if she wants another card
    * @return whether another card is wanted */
  public boolean wantsACard()
  { return card_count < hand_size; }

  /** receiveCard  accepts a card and adds it to the hand
    * @param c - the card to be added */
  public void receiveCard(Card c)
  { hand[card_count] = c;
    card_count = card_count + 1;
  }

  /** showCards gives the player's hand to others to see
    * @return (the address of) the hand of cards */
  public Card[] showCards()
  { return hand; }
}
/** Dealer deals cards to players */
public class Dealer
{ private CardDeck deck = new CardDeck();  // the deck the dealer uses

  public Dealer() { }

  public void dealTo(Player p)
  { boolean processing = true;
    while ( processing )
          { if ( p.wantsACard()  &&  deck.moreCards() )
                 { p.receiveCard(deck.newCard()); }
            else { processing = false; }
          }
  }
}
public class TestCard
{ public static void main(String[] args)
  { int hand_size = 5;
    Dealer d = new Dealer();
    Player p = new Player(hand_size);
    d.dealTo(p);
    Card[] hand = p.showCards();
    for ( int i = 0;  i != hand_size;  i = i+1 )
        { System.out.println( hand[i].countOf() + " of " + hand[i].suitOf()); }
  }
}

5.

public class TestCard2
{ public static void main(String[] args)
  { int hand_size = 5;
    int number_of_players = 3;

    Dealer d = new Dealer();
    // create the players:
    Player[] player = new Player[number_of_players];
    for ( int i = 0;  i != number_of_players;  i = i+1 )
        { player[i] = new Player(hand_size); }
    // make the dealer deal to the players:
    for ( int i = 0;  i != number_of_players;  i = i+1 )
        { d.dealTo(player[i]); }
    // make the players show their cards:
    for ( int i = 0;  i != number_of_players;  i = i+1 )
        { System.out.println("Player " + i + " holds:");
          printCards(player[i].showCards()); }
  }

  private static void printCards(Card[] hand)
  { for ( int i = 0;  i != hand.length;  i = i+1 )
        { System.out.println( hand[i].countOf() + " of " + hand[i].suitOf()); }
    System.out.println();
  }
}

Two-Dimensional Arrays

1.

int[][] m = new int[12][14];
for ( int i = 0;  i != 12;  i = i + 1 )
    { for ( int j = 0;  j != 14;  j = j + 1 )
          { m[i][j] = i * j; }
    }

2.

String[][] m = new String[7][15];
for ( int i = 0;  i != 7;  i = i + 1 )
    { for ( int j = 0;  j != 15;  j = j + 1 )
          { m[i][j] =  "Element " + i + " " + j; }
    }

3.1.

for ( int i = 0;  i != 4;  i = i+1 )
    { for ( int j = 0;  j != 4;  j = j+1 )
          { if ( j <= i )
                 { r[i][j] = j + 1; }
            else { r[i][j] = 0; }
          }
    }

3.2

for ( int i = 0;  i != 4;  i = i+1 )
    { for ( int j = 0;  j != 4;  j = j+1 )
          { if ( j >= i )
                 { r[i][j] = i + j + 1; }
            else { r[i][j] = 0; }
          }
    }

3.3

for ( int i = 0;  i != 4;  i = i+1 )
    { for ( int j = 0;  j != 4;  j = j+1 )
          { r[i][j] = (i + j + 1) % 2; }
    }

4.

public class VoteCount4
{
  public static void main(String[] args)
  { int num_candidates = 4;                    // how many candidates
    int num_regions = 3;
    int[][] election = new int[num_regions][num_candidates]; // holds the votes
    // collect the votes until a  -1  is read:
    boolean processing = true;
    while ( processing )
          // all legal votes read so far have been tallied in  votes
          { int r = new Integer(JOptionPane.showInputDialog
                 ("Type your region (0,1,2): ").intValue();
            if ( r == -1 )
                 { processing = false; }
            else if ( r < 0  ||  r > num_regions )
                 { System.out.println("VoteCount error: region " + r); }
            else { int v = new Integer(JOptionPane.showInputDialog
                            ("Vote for (0,1,2,3): ").intValue();
                   if ( v >= 0  &&  v < num_candidates )
                        { election[r][v] = election[r][v] + 1; }
                   else { System.out.println("VoteCount error: vote " + v); }
                 }
          }
    // print the totals:
    int[] candidate = new int[num_candidates];
    System.out.println("Votes by region and candidate: ");
    for ( int i = 0;  i != num_regions;  i = i+1 )
        { for ( int j = 0;  j != num_candidates;  j = j+1 )
              { candidate[j] = candidate[j] + election[i][j];
                System.out.print(election[i][j] + "  ");
              }
          System.out.println();
        }
    // print candidates' totals:
    for ( int i = 0;  i != num_candidates;  i = i+1 )
        { System.out.println("Candidate" + i + " has " + candidate[i]); }
  }
}

5.

/** BankDatabase  implements a database of bank records */
public class BankDatabase
{ private BankAccount1[][] database = new BankAccount1[100][100];
    // Note: first two digits of a four-digit key are used as row number;
    //       last two digits are used as column number.

  /** Constructor  BankDatabase  initializes the database */
  public BankDatabase() { }

  /** insert inserts a new record into the database.
    * @param r - the record
    * @return true, if record added; return false if record not added because
    *   another record with the same key already exists in the database */
  public boolean insert(BankAccount1 r)
  { boolean success = false;
    int prefix = r.keyOf() / 100;
    int suffix = r.keyOf() % 100;
    if ( prefix >= 0  &&  prefix <= 99  &&  suffix >= 0  &&  suffix <= 99 )
         { if ( database[prefix][suffix] == null )
              { database[prefix][suffix] = r;
                success = true;
              }
         }
    return success;
  }

  /** find  locates a record in the database based on a key
    * @param key - the key of the desired record
    * @return (the address of) the desired record;
    *  return  null if record not found.  */
  public BankAccount1 find(int k)
  { BankAccount1 answer = null;
    int prefix = k / 100;
    int suffix = k % 100;
    if ( prefix >= 0  &&  prefix <= 99  &&  suffix >= 0  &&  suffix <= 99 )
       { answer = database[prefix][suffix]; }
    return answer;
  }

  /** delete removes a record in the database based on a key
    * @param key - the record's key (identification)
    * @return true, if record is found and deleted; return false otherwise  */
  public boolean delete(int k)
  { ... database[prefix][suffix] = null ... }
}
The main advantage of this implementation is that finding records is simple; the main disadvantage is that the database is fixed at 10,000 elements, where many of its elements are occupied by null.

7.

public int[][] mirror(int[][] m)
{ int rows = m.length;
  int cols = m[0].length;  // assume that all rows of  m  have same length!
  int[][] n = new int[cols][rows];
  for ( int i = 0;  i != rows;  i = i+1 )
      { for ( int j = 0;  j != cols;  j = j+1 )
                 { n[i][j] = m[(rows-i)-1][(cols-j)-1]; }
           }
      }
  return n; }

Case Study: Slide-Puzzle Game

1.

public class Test1
{ public static void main(String[] args)
  { SlidePuzzleBoard board = new SlidePuzzleBoard(4);

    PuzzlePiece[][] r = board.contents();
    printContents(r);

    board.move(1);
    printContents(board.contents());

    board.move(4);
    printContents(board.contents());
  }

  /** printContents displays the puzzle pieces in board  r */
  private static void printContents(PuzzlePiece[][] r)
  { for ( int i = 0;  i != r.length;  i = i+1 )
         { for ( int j = 0;  j != r[i].length;  j = j+1 )
               { if ( r[i][j] == null )
                    { System.out.print("X "); }
                 else { System.out.print( r[i][j].valueOf() + " " ); }
               }
           System.out.println();
         }
    System.out.println();
  }
}

2.

public class PuzzleWriter2
{ private SlidePuzzleBoard board;   // the board that is displayed
  private int size;                 // the board's size

  /** Constructor PuzzleWriter2 initializes the writer.
    * @param b - the slide puzzle that is displayed
    * @param s - the size of the slide puzzle, e.g., 4 means  4 x 4  */
  public PuzzleWriter2(SlidePuzzleBoard b, int s)
  { board = b;
    size = s;
  }

/** displayPuzzle displays the current state of the slide puzzle. */
  public void displayPuzzle()
  { PuzzlePiece[][] r = board.contents();  // get current contents of board
    for ( int i = 0;  i != size;  i = i+1 )
         { for ( int j = 0;  j != size;  j = j+1 )
               { if ( r[i][j] == null )
                    { System.out.print("X "); }
                 else { System.out.print( r[i][j].valueOf() + " " ); }
               }
           System.out.println();
         }
    System.out.println();
  }

 /** printError displays an error message.
    * @param s - the error message */
  public void printError(String s)
  { System.out.println("PuzzleWriter2 error: " + s ); }
}

Chapter 9: Programming to Interfaces

Interfaces

1.1. The class implements Convertible---the data type, int, and not the name, j, of the formal parameter is used to match convert to its header line in the interface.

1.2. The class does not implement the interface because the return data type of convert must be double.

1.3. The class does not implement the interface because it lacks the clause, implements Convertible, in its header line.

2.1. The interface is used correctly.

2.2. There are three errors: the phrase, uses Convertable, should be deleted; the interface name cannot be used with the method invocation, convert; the parameter to convert must be an integer, not a double.

2.3. There is one error: one cannot construct a new object from an interface definition; the object must be constructed from the class that implements the interface.

4.1. BankAccount's header line is modified like this:

public class BankAccount implements BankAccountSpecification
BankWriter is modified like this:
public class BankWriter extends JFrame
{ private int WIDTH = 300;  // width and depth of displayed window
  private int DEPTH = 200;
  private BankAccountSpecification bank;
  private String last_transaction = "";

  /** Constructor BankWriter initializes the writer
    * @param title - the title bar's text
    * @param b - the (address of) the bank account displayed by the Writer */
  public BankWriter(String title, BankAccountSpecification b)
  { ... }
  ...
}
When compiled, the class generates this error:
BankWriter.java:30: Method getBalance() not found in interface BankAccountSpecification.
    g.drawString("Current balance = $" + unconvert(bank.getBalance()),

4.2. This revised interface lets all the classes compile without errors:

/** BankAccountSpecification specifies the expected behavior of a
  *  bank account.   */
public interface BankAccountSpecification
{ /** deposit adds money to the account
    * @param amount - the amount of the deposit, a nonnegative integer  */
  public void deposit(int amount);

  /** withdraw removes money from the account, if possible
    * @param amount - the amount of the withdrawal, a nonnegative integer
    * @return true, if the the withdrawal was successful;
    *  return false, otherwise.  */
  public boolean withdraw(int amount);

 /** getBalance reports the current account balance
   * @return the balance */
  public int getBalance();
}

4.3 Because of the use of the BankAccountSpecification, the replacement of class BankAccount by class SillyAccount can be made in AccountManager without changing any of the other components in the application.

5.1. Here is the interface:

/** MovingObjectBehavior lists the behaviors of a moving object */
public interface MovingObjectBehavior
{ /** xPosition returns the object's current horizontal position */
  public int xPosition();

  /** yPosition returns the object's current vertical position */
  public int yPosition();

  /** radiusOf returns the object's size */
  public int radiusOf();

  /** move moves the object one step */
  public void move();
}
Notice how the attributes in Table 6, Chapter 7, suggest the accessor methods in the interface. All occurrences of MovingBall are replaced by MovingObjectBehavior in class BallWriter and class BounceController.

5.2

 BounceController ------> AnimationWriter
  runAnimation()           paint(Graphics g)
   |                       |              |
                           v              v
   |                BallWriter           BoxWriter
                     paint(Graphics g)     paint(Graphics g)
   |                  |                               |
                                                      |
   |                  |                               |
                      v                               |
   + - - - - - - -> MovingObjectBehavior     |
                                     ^                |
                                     -                |
                                     |                |
                                                      |
                                     |                v
                                MovingBall --------> Box

5.3 The class looks like this:

import java.awt.*;
/** BounceTheBall constructs and starts the objects in the animation.  */
public class BounceTheBall
{ public static void main(String[] args)
  { // construct the model objects:
    int box_size = 200;
    int balls_radius = 6;
    Box box = new Box(box_size);
    ThrobbingBall ball = new ThrobbingBall();
    // construct the output-view objects:
    BallWriter ball_writer = new BallWriter(ball, Color.red);
    BoxWriter box_writer  = new BoxWriter(box);
    AnimationWriter writer
             = new AnimationWriter(box_writer, ball_writer, box_size);
    // construct the controller and start it:
    new BounceController(ball, writer).runAnimation();
  }
}
Here is the revised class diagram:
 BounceController ------> AnimationWriter
  runAnimation()           paint(Graphics g)
   |                       |              |
                           v              v
   |                BallWriter           BoxWriter
                     paint(Graphics g)     paint(Graphics g)
   |                  |                               |
                                                      v
   |                  |                              Box
                      v                             
   + - - - - - - -> MovingObjectBehavior  
                                     ^            
                                     -           
                                     |          
                                               
                                     |        
                                ThrobbingBall

Case Study: Databases

1. Here is the revised version of class BankAccount1; the other examples are revised similarly.

/** BankAccount1 models a bank account with an identification key */
public class BankAccount1 implements Record
{ private int balance;  // the account's balance
  private IntegerKey key;      // the identification key; see Figure 5, Ch. 9.

  /** Constructor BankAccount1 initializes the account
    * @param initial_amount - the starting account balance, a nonnegative.
    * @param id - the account's identification key  */
  public BankAccount1(int initial_amount, int id)
  { balance = initial_amount;
    key = new IntegerKey(id);
  }

  /** deposit adds money to the account.
    * @param amount - the amount of money to be added, a nonnegative int */
  public void deposit(int amount)
  { balance = balance + amount; }

  /* balanceOf reports the current account balance
   * @return the balance */
  public int balanceOf()
  { return balance; }

  /* keyOf returns the account's key
   * @return the key */
  public Key keyOf()
  { return key; }
}

2.

/** Book models a library book that is identified by a key */
public class Book implements Record
{ private Key id_number;
  private String title;
  private String author;

  /** Constructor Book constructs the book.
    * @param num - the book's id number
    * @param a - the book's author
    * @param t - the book's title */
  public Book(Key num, String a, String t)
  { id_number = num;
    title = t;
    author = a;
  }

  /** getKey returns the key that identifies the record
    * @return the key  */
  public Key getKey ()
  { return id_number; }

  /** getTitle returns the book's title 
   * @return the title */
  public String getTitle()
  { return title; }

  /** getAuthor returns the book's author 
   * @return the author */
  public String getAuthor()
  { return author; }
}
Here is a U.S.-Library-of-Congress-style key for library books:
/** CatalogNumber models a Library-of-Congress-style id number,
  *  consisting of a letter code concatenated to a decimal number */
public class CatalogNumber implements Key
{ private String letter_code;  // the letter code, e.g.,  "QA"
  private double number_code;  // the number code, e.g.,  76.884

  /** Constructor CatalogNumber constructs a catalog number
    * @param letters - the letter code, e.g.,  "QA"
    * @param num - the decimal number code, e.g.,  76.884 */
  public CatalogNumber(String letters, double num)
  { letter_code = letters;
    number_code = num;
  }

  /** equals returns whether the catalog number held within this object
    *  is identical to the catalog number held within  j
    * @param j - the other catalog number
    * @return true, if this catalog number equals j's;
    *   return false, otherwise */
  public boolean equals(Key j)
  { CatalogNumber c = (CatalogNumber)j;   // cast  j  to its true type
    String s = c.getLetterCode();
    double d = c.getNumberCode();
    return ( s.compareTo(letter_code) == 0   // the exact same strings
             &&  d == number_code );
  }

  /** getLetterCode returns the letter code part of this catalog number
    * @return the letter code, e.g.,  "QA"  */
  public String getLetterCode()
  { return letter_code; }
  
  /** getNumberCode returns the number code part of this catalog number
    * @return the number code, e.g.,  "76.884"  */
  public double getNumberCode()
  { return number_code; }
}

Inheritance

1.1. This is erroneous, because class Person does not have a getAddress method. 1.2. This is acceptable, because class PersonAddress inherits a getName method. The string, ethel prints. 1.3. This is acceptable; the method compares fred to fred and prints true. 1.4. This is acceptable; the method compares fred to ethel and prints false. 1.5. This is erroneous, because the argument, p, has data type Person, which does not match the formal parameter type PersonAddress. (See the next section for details.)

Reference Types, Subtypes, and instanceof

1.1. This is acceptable, because q's data type is a subtype of Person. false is printed. 1.2. This is acceptable, because the assignment to x is acceptable, and the Java compiler notes that x's value must own a getName method. ethel is printed. 1.3. This is erroneous, and the Java compiler will complain about the assignment statement, which is badly typed. 1.4. The Java compiler will state that the method invocation, x.getAddress(), is erroneous, because x has data type Person, and Person objects are not guaranteed to have getAddress methods. 1.5. The is erroneous, because the actual parameter, p has data type Person, which is not a subtype of PersonAddress.

2. The private variable, x, within C, cannot be referenced within class D, even though D is a subclass.

3.1.

/** IntegerKey models an integer key  */
public class IntegerKey implements Key
{ private int id;  // the key number

  /** IntegerKey  constructs the key object
    * @param j - the integer held as the key */
  public IntegerKey(int j)
  { id = j; }

  /** valOf  returns the integer value of the key */
  public int valOf()
  { return id; }

  public boolean equals(Key another_key)
  { boolean answer;
    // ask if  another_key's  run-time data type is  IntegerKey:
    if ( another_key instanceof IntegerKey )
         { int m = ((IntegerKey)another_key).valOf();
           answer = (id == m);
         }
    else // another_key  is not an  IntegerKey, so don't compare:
         { answer = false; }
    return answer;
  }


  public boolean lessthan(Key another_key)
  { boolean answer;
    // ask if  another_key's  run-time data type is  IntegerKey:
    if ( another_key instanceof IntegerKey )
         { int m = ((IntegerKey)another_key).valOf();
           answer = (id < m);
         }
    else // another_key  is not an  IntegerKey, so don't compare:
         { answer = false; }
    return answer;
  }
}

3.2

/** StringKey models a string key  */
public class StringKey implements Key
{ private String id;  // the key

  /** StringKey  constructs the key object
    * @param j - the string held as the key */
  public StringKey(String j)
  { id = j; }

  /** valOf  returns the string value of the key */
  public String valOf()
  { return id; }

  public boolean equals(Key another_key)
  { boolean answer;
    if ( another_key instanceof StringKey )
         { String m = ((StringKey)another_key).valOf();
           answer = (id.equals(m));
         }
    else { answer = false; }
    return answer;
  }

  public boolean lessthan(Key another_key)
  { boolean answer;
    if ( another_key instanceof StringKey )
         { String m = ((StringKey)another_key).valOf();
           answer = (id.compareTo(m) < 0 );  // the  compareTo  method
            // returns a negative when the first string is < the second one
         }
    else { answer = false; }
    return answer;
  }
}

4.1. Only C <= I holds true, because of the phrase, class C implements I---the Java compiler must be explicitly told about all subtype relationships.

4.2. These expressions evaluate to true: x instanceof I, x instanceof C, y instanceof I, y instanceof C, z instanceof I, and z instanceof C, because the run-time data type of the object is used to check the instanceof operation---the data types of the variable names, x, y, and z do not matter!

5. The needed casts are marked by (*):

I x = new C();
x.f(21);
((C)x).g();   // (*)
C y = (C)x;  // (*)
y.g();
if ( x instanceof C )
   { ((C)x).g(); }  // (*)

6. I b = a is disallowed, because D is not a subtype of I.

if ( a instanceof C ) is disallowed, because the data type of of variable a is D, and D is not a subtype of C, so the Java compiler knows already that is impossible for the operation to return the result, true. It reports an error to warn the programmer that the instanceof operation is used in a foolish way.

Abstract Classes

1.

public class Dealer
{ private CardDeck deck;

  public Dealer()
  { deck = new CardDeck(); }

  public void dealTo(CardPlayerBehavior p)
  { boolean processing = true;
    while ( processing )
          { if ( p.wantsACard()  &&  deck.moreCards() )
                 { p.receiveCard(deck.newCard()); }
            else { processing = false; }
          }
  }
}

2. In the above solution, replace CardPlayerBehavior by CardPlayer. Clearly, the coding of class Dealer is no more simpler or complex. The main drawback of using the abstract class instead of the interface as the formal parameter is that all future uses of class Dealer in applications must use the codings of the receiveCard and showCards methods, even though these have nothing to do specifically with the Dealer.

3.

public class Rectangle extends Polygon
{ private Point[] corners = new Point[4];
  private int my_width;
  private int my_height;

  public Rectangle(Point location, int width, int height)
  { super(location);
    my_width = width;
    my_height = height;
    int x_start = location.xPosition();
    int y_start = location.yPosition();
    corners[1] = location;
    corners[2] = new Point(x_start + width, y_start);
    corners[3] = new Point(x_start, y_start + height);
    corners[4] = new Point(x_start + width, y_start + height);
    super.setCorners(corners);
   }

  public int widthOf()
  { return my_width; }

  public int depthOf()
  { return my_height; }
}

public class Circle extends Shape
{ private int my_radius;

  public Circle(Point location, int radius)
  { super(location);
    my_radius = radius;
  }

  public int widthOf()
  { return my_radius; }

  public int depthOf()
  { return my_radius; }
}

Class Hierarchies

1. Classes Point and Shape are revised so that their header lines state extends Form. Classes Line and Straight might be written as follows:

public abstract class Line extends Form
{ private Point start;
  private Point end;

  public Line(Point s, Point e)
  { start = s;
    end = e;
  }

  public Point startOf()
  { return start; }

  public Point endOf()
  { return end; }
}

public class Straight extends Line
{
  public Straight(Point start, Point end)
  { super(start, end); }
}

2.1. One of many possible hierarchies might begin as follows:

SchoolPeople
|
+--Staff
|  |
|  +--Administrative
|  |
|  +--Teaching
|     |
|     +--Professor
|     |
|     +--Instructor
|     |
|     +-- ...
|  
+--Student
   |
   +--Undergraduate
   |  |
   |  +--- ...
   |
   +--Graduate
      |
      +--- ...

Case Study: An Adventure Game

1.

/** Dungeon models a dungeon into which everyone can enter and no one can
  * escape  */
public class Dungeon implements RoomBehavior
{ /** Constructor Dungeon builds the room. */
  public Dungeon() { }

  public boolean enter(PlayerBehavior p)
  { return true; }

  public void exit(PlayerBehavior p)
  { }

  public PlayerBehavior occupantOf()
  { return null; }
}

2.

RoomBehavior[] room = new RoomBehavior[3];
room[0] = new BasicRoom("kitchen", "pasta");
room[1] = new BasicRoom("lounge", "swordfish");
room[2] = new Dungeon();

Explorer harpo = new Explorer("Harpo Marx", "swordfish");
for ( int i = 0;  i != 3;  i = i+1 )
    { boolean success = harpo.explore(room[i]); 
      System.out.println("Entered room " + i + "? " + success);
      if ( success )
         { harpo.exitRoom(); }
    }

3. A example is:

RoomBehavior[] room = new RoomBehavior[2];
room[0] = new BasicRoom("kitchen", "swordfish");
room[1] = new BasicRoom("lounge", "swordfish");
Explorer harpo = new Explorer("Harpo Marx", "swordfish");
boolean success = harpo.explore(room[0]);
boolean success = harpo.explore(room[1]);
The object named harpo occupies two rooms simultaneously.

Revise the enter method to read:

public boolean enter(PlayerBehavior p)
  { boolean result = false;
    if ( occupant == null  
         &&  p.locationOf() == null;  //  p  occupies no room at the moment
         &&  secret_word.equals(p.speak())  )
       { occupant = p;
         result = true;
       }
    return result;
  }

Interfaces and Inheritance Together

1.

/** GoldCoin models a treasure */
public class GoldCoin implements TreasureProperty
{ private int value;  // the coin's value

  public GoldCoin(int v)
  { value = v; }

  public String contentsOf()
  { return "a gold coin worth " + value; }
}
The vault might appear:
Vault v = new Vault("lounge", "swordfish", new GoldCoin(10));

2.

/** Explorer2 models a player who explores rooms and tries to take
  *  treasures from them   */
public class Explorer2 implements PlayerBehavior
{ private String my_name;
  private String my_secret_word;  // a password for entering rooms
  private RoomBehavior where_I_am_now;  // the room this object occupies

  /** Constructor Explorer2 builds the Player
    * @param name - the player's name
    * @param word - the password the player can speak */
  public Explorer2(String name, String word)
  { my_name = name;
    my_secret_word = word;
    where_I_am_now = null;  // player does not start inside any room
  }

  public String speak()
  { return my_secret_word; }

  /** exitRoom causes the player to leave the room it occupies, if any */
  public void exitRoom()
  { if ( where_I_am_now != null )
       { where_I_am_now.exit(this);   // exit the room
         where_I_am_now = null;
       }
  }

  public boolean explore(RoomBehavior r)
  { if ( where_I_am_now != null )
       { exitRoom(); }  // exit current room to go to room  r:
    boolean went_inside = r.enter(this);  // ``this'' means ``this object''
    if ( went_inside )
       { where_I_am_now = r;
         if ( r instanceof Treasury )  // might this room hold a treasure?
            { TreasureProperty treasure = ((Treasury)r).yieldTreasure(this); }
       }
    return went_inside;
  }

  /** locationOf returns the room that is occupied by this player */
  public RoomBehavior locationOf()
  { return where_I_am_now; }
}

3.

/** TreasureHunter explores rooms and saves all the treasures it extracts
  *  from the rooms.  */
public class TreasureHunter extends Explorer implements TreasureHunterBehavior
{ private TreasureProperty[] my_treasures;
  private int count;

  /** Constructor  TreasureHunter  constructs the player
    * @param name - the player's name
    * @param word - the password the player can speak
    * @param how_many - the number of treasures the player can hold.  */
  public TreasureHunter(String name, String word, int how_many)
  { super(name, word);
    my_treasures = new TreasureProperty[how_many];
    count = 0;
  }

  /** takeTreasure  tries to extract the treasure from the current room
    *  that this player occupies.
    * @return true if the treasure was succesfully extracted from the
    *   room and saved by this player; return false otherwise */
  public boolean takeTreasure()
  { boolean result = false;
    RoomBehavior r = locationOf();   // what room do I occupy?
    if ( r != null )
       { if ( r instanceof Treasury )  // might this room hold a treasure?
            { TreasureProperty treasure = ((Treasury)r).yieldTreasure(this);
              if ( treasure != null )
                 { if ( count != my_treasures.length )
                      { my_treasures[count] = treasure;
                        count = count + 1;
                        result = true;
                      }
                   else { System.out.println("oops! lost the treasure!"); }
                 }
            }
       }
    return result;
  }

  /** getTreasures  returns an array of all the treasures located so far
    *  by this player
    * @return the array of treasures  */
  public TreasureProperty[] getTreasures()
  { TreasureProperty[] answer = new TreasureProperty[count];
    for ( int i = 0;  i != count;  i = i+1 )
        { answer[i] = my_treasures[i]; }
    return answer;
  }
}

4.

public class RuthlessHunter extends TreasureHunter
{ /** Constructor  RuthlessHunter  constructs the player
    * @param name - the player's name
    * @param word - the password the player can speak
    * @param how_many - the number of treasures the player can hold.  */
  public RuthlessHunter(String name, String word, int how_many)
  { super(name, word, how_many); }

  /** explore  attempts to enter a room and explore it.  If the room is
    * successfully entered, then an attempt is made to take the treasure
    * from the room and keep it (if the room indeed holds a treasure).
    * @param r - the room that will be explored
    * @return whether the room was successfully entered */
  public boolean explore(RoomBehavior r)
  { boolean success = super.explore(r);   // invoke the older version of 
               //  explore,  the one located in the superclass  Explorer
    if ( success )
       { takeTreasure(); }
    return success;
  }
}
It is crucial to state, super.explore(r), in the body of explore so that the method in class Explorer is executed and the method in class RuthlessHunter is not restarted.