Chapter 5: Booleans and Flow Control

On the bottom of p. 114, the last four operators are misidentified. The table should read:

x < yLess thanTrue if x is less than y, otherwise false.
x > yGreater than True if x is greater than y, otherwise false.
x <= yLess than or equal toTrue if x is less than or equal to y, otherwise false.
x >= yGreater than or equal toTrue if x is greater than or equal to y, otherwise false.
x == yEqual toTrue if x equals y, otherwise false.
x != yNot equal toTrue if x is not equal to y, otherwise false.

On p. 117, both programs 5.1 and 5.2 print "The strings are the same." in Java 1.0.2 and later. The program works as advertised in Java 1.0.1 and earlier. An optimization was introduced in Java 1.0.2 where identical String literals are only stored once in the .class file. This isn't a problem for most code because strings are immutable. Regrettably Example 5.1 is one of the exceptions. Thus if you run Program 5.1 as written in the book, the output will claim the strings are the same because, in Java 1.0.2 and later, they really are the same object. There's only one string here, not two, although there are two references to it.

If you use constructors to create the strings, then the program behaves as advertised. There are now two strings with one reference to each rather than one string to which two references exist. Thus program 5.1 should now bet this:

class JackandJill {

  public static void main(String args[]) {
  
    String s1 = new String("Jack went up the hill.");
    String s2 = new String("Jack went up the hill.");
    
    if (s1 == s2) {
      System.out.println("The strings are the same.");
    }
    else if (s1 != s2) {
      System.out.println("The strings are different.");
    }
  
  }

}
Program 5.2 should look like this:

class JackandJill {

  public static void main(String args[]) {
  
    String s1 = new String("Jack went up the hill.");
    String s2 = new String("Jack went up the hill.");
    
    if (s1.equals(s2)) {
      System.out.println("The strings are the same.");
    }
    else {
      System.out.println("The strings are different.");
    }
  
  }

}
Incidentally, these revised versions work in Java 1.0 and 1.0.1 as well as Java 1.0.2 and later.

On p. 124 there should be semicolons after the while statements, that is

 do {
  // what you want to do
 } while (condition);

class CountToTen {

  public static void main (String args[]) {

    int i=1;    
    do {  
      System.out.println(i);
      i = i + 1;
   } while (i <= 10);
 }

}
On p. 128, Program 5.10 should double the number of grains after adding them to the total rather than before. The correct code and output is as follows:

class CountWheat  {

  public static void main (String args[]) {
  
    int i, j, k;

    j = 1;
    k = 0;

    for (i=1; i <= 64; i++) {
      k += j;
      j *= 2;
      System.out.print(k + "\t  ");
      if (i%4 == 0) System.out.println();
    } 
    System.out.println("All done!");

  }

}
Here's the output:

% javac CountWheat.java
% java CountWheat
1	  3	  7	  15	  
31	  63	  127	  255	  
511	  1023	  2047	  4095	  
8191	  16383	  32767	  65535	  
131071	  262143	  524287	  1048575	  
2097151	  4194303	  8388607	  16777215	  
33554431	  67108863	  134217727	  268435455	  
536870911	  1073741823	  2147483647	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
-1	  -1	  -1	  -1	  
All done!
%
On p. 129, Example 5.11 has the same problem. Here's the correct version:

class CountWheat {

  public static void main (String args[]) {
  
    int i;
    double j, k;
  
    j = 1.0;
    k = 0.0;
  
    for (i=1; i <= 64; i++) {
      k += j;
      System.out.print(k + "\t  ");
      if (i%4 == 0) System.out.println();
      j *= 2.0;
    } 
    System.out.println("All done!");

  }

}
Finally, on p. 131 Example 5.12 also increments before it adds instead of adding before incrementing. The correct version of its code and ouptut is

class CountWheat  {

  public static void main (String args[]) {
  
    int i, j, k;

    j = 1;
    k = 0;

    for (i=1; i <= 64; i++) {
      k += j;
      if (k <= 0) {
        System.out.println("Error: Overflow");
        break;
      }
      System.out.print(k + "\t  ");
      if (i%4 == 0) System.out.println();
      j *= 2;
    } 
    System.out.println("All done!");

  }

} 
Here's the output:

% java CountWheat
1	  3	  7	  15	  
31	  63	  127	  255	  
511	  1023	  2047	  4095	  
8191	  16383	  32767	  65535	  
131071	  262143	  524287	  1048575	  
2097151	  4194303	  8388607	  16777215	  
33554431	  67108863	  134217727	  268435455	  
536870911	  1073741823	  2147483647	  Error: Overflow
All done!
On p. 133-134 I really messed up the details of the switch statement. The labels of a case statement can only be literals or final static int fields. They cannot be variables or expressions as I claim in the book. This has to do with how Java compiles a switch statement in the virtual machine. The actual numeric values of the case statements are embedded in the byte code. This makes switch statements much more efficient than they otherwise would be. Here's how it should read:

Java has a shorthand for these types of multiple if statements, the switch-case statement. Here's how you'd write the above using a switch statement:

switch (x) {
  case 0: 
    // do thing 0...;
    break;
  case 1: 
    // do thing 1...;
    break;
  case 2: 
    // do thing 2...;
    break;
  case 3: 
    // do thing 3...;
    break;
  default: 
   // do thing 4...;
}
In this fragment x must be a variable or expression that can be cast to an int without loss of precision. This means the variable must be or the expression must return an int, byte, short or char. x is compared with the value of each the case statements in succession. This fragment compares x to literals, but these too could be variables or expressions as long as the variable or result of the expression is an int, byte, short or char.

Once a case statement is matched all executable statements following it are executed including those in subsequent, unmatched case statements. This can trigger decidedly unexpected behavior. Therefore it's common practice to include the break statement at the end of each case block. If the breaks weren't included in the above code fragment and case 1 were matched, then not only thing 1 but also thing 2, thing 3, and thing 4 would be performed. It's important to remember that the switch statement doesn't end when one case is matched and its action performed. The program continues to look for additional matches unless specifically told to break.

Finally if no cases are matched, the default action is triggered.

It is not true that multiple case statements are matched as I claimed in the book. Thanks are due to Bob Follek for catching these mistakes.

On p. 136 the last two paragraphs about the ?: operator are completely wrong. Replace them with the following:

The conditional operator only works for assigning a value to a variable, using a value in a method invocation, or in some other way that indicates the type of its second and third arguments. For example, consider the following

if (name.equals("Rumplestiltskin")) {
  System.out.println("Give back child");
}
else {
  System.out.println("laugh");
}
This may not be written like this:

name.equals("Rumplestiltskin") ? System.out.println("Give back child"); :System.out.println("Give back child");;
First of all, both the second and third arguments are void. Secondly, no assignment is present to indicate the type that is expected for the second and third arguments (though you know void must be wrong).

The first argument to the conditional operator must have or return boolean type and the second and third arguments must return values compatible with the value the entire expression can be expected to return. You can never use a void method as an argument to the ? : operator.


Corrections to Other Chapters
[ Cafe Au Lait | Books | Trade Shows | Links | FAQ | Tutorial | User Groups ]
Copyright 1997, 2002 Elliotte Rusty Harold
elharo@metalab.unc.edu
Last Modified December 31, 2002