Statements

Introduction to Computer Science I-III
ICS-21/-22/-23


Introduction Statements are like complete imperative sentences in Java: each commands Java to perform some action. Just as we said that Java evaluates expressions, we say that Java executes statements. We already have studied the Java declaration statement, which declares variables (and optionally initializes them). We will classify many statements as control structures: such statements control (conditionally/by repetition) the execution of other statements.

In this lecture we will first learn how to write the simplest kind of statement in Java, the expression statement, and the simplest control structure in Java, the block statement, We will also begin discussing two analysis tools: hand simulation via trace tables, and statement boxing (which is the statement equivalent of oval diagrams for expressions).

Then we will learn about Java's most important control structures, starting with if statements for decisions and simple for/break statements for looping. We will extend our analysis tools, trace tables and boxing, to cover these statements. Eventually we will generalize the for loop and cover two variants of looping: while and do statements.

Finally, we will begin learning about Java's try-catch statement: a control structure that programmers use to process exceptions. Sometimes when an operator or method fails by throwing an exception, it does not denote a failure of the program, but is just a signal to the program that it must take some special action. We will continue exploring the use of the try-catch statement in our next lecture, in the context of file I/O.

Thus, we can summarize the language features that we learn in this lecture by

    statement <= local-variable-declaration-statement |
                           expression-statement |
                           block-statement |
                           if-statement |
                           for-statement |
                           break-statement |
                           while-statement |
                           do-statement |
                           try-catch--statement

We will explore the semantics of the following control structures in detail.

  • block: execute a sequence of statements in order
  • if: decide which statement to execute
  • for/while/do loop: repeatedly execute a statement
  • break: terminate execution of a loop
  • try-catch: execute a sequence of statements; if one throws an exception, execute an alternative sequence of statements

Declaring Variables
(review and extension)
We have already discussed most aspects of the declaration-statement in depth. Recall that the simplest declarations start with the type of the variable, followed by the name of the variable, ended by a semicolon. In more complex variable declarations, we can specify multiple variable names (each declared to be the same type, the one that starts the declaration), and each variable can be initialized to a value.

We originally specified the EBNF for each declarator as

    variable-declarator <= identifier [=expression]

and said that expression could be only a literal (the only kind of expression we knew at that time). Now we know about much more complicated expressions, including literals, variables, operators, and methods; we can use all these to declare the initial values for variables. Pragmatically, most intializations use either literals or calls to the Prompt methods, but other form arise less frequently. Here are some examples.

  int a = 0;
  int b = Prompt.forInt("Enter b");
  int c = b;
  int d = Math.max(c,1) + 1;

Here the declation of a is intialized using the literal 0; the declation of b is initialized to the result returned by calling the Prompt.forInt method; the declation of c is initialized using b, the value of a previous declared and intialized variable; finally, the declation of d is initialized to the result returned by an expression involving a method call, operator, literal, and the value of a previously declared variable (c).

In addition, Java allows the keyword final to appear optionally in

    local-variable-declaration-statement <= [final] type variable-declarators ;

Semantically, if such a declaration includes final, then all the variables that it declares must include an initializer and the value they store must never be changed. That is, we cannot use any state-change operators on final variables. If we declared

  final double PI = 3.14159265358979323846;
Then Java would detect and report an error if we later wrote PI = 3.0 Thus, we can use final to declare named constants, whose value is guaranteed by Java not to change.

Expression Statements We can make an expression into expression statement by appending a semicolon at its end. Such a statement tells Java to evaluate the expression. The EBNF rule for expression statements is simply

expression-statement <= [expression] ;

In fact, by discarding the option, we can write just the semicolon as the simplest kind of expression statement: it is a statement that "does nothing".

But Java imposes one important syntax constraint on expression statements: if the expression option is included

  • The expression must apply a state-change operator or a method call last in the expression.
Thus, the following statements are all legal examples of expression statements: each satisfies this syntax constraint..
    average = (score1 + score2 + score3) / 3;
    gameCount++;
    counter1 = counter2 = counter3 = 0;
    System.out.println("You have played "+gameCount+" games");
Recall that most state-change operators have very low precedence, so they will naturally be evaluated last in an expression statement. Methods whose prototype lists void as the type of the result don't return a result anyway. Such methods (e.g., System.out.println) are often called last when evaluating expression statements.

Note that x+1; is NOT a legal expression statement: the last operator that it applies is +, which computes a value but does not process this value further: e.g., doesn't store it anywhere; doesn't print it. Writing such an expression serves no purpose, and the Java compiler detects and reports an error in this case.

Finally, notice that this syntax constraint still allows Prompt.forInt("Enter values"); as a legal expression statement, even though this method returns an int result which is not processed further.


Block Statements
(and scope)
Some airlines restrict you to one carry-on bag. If you show up with three small bags, they won't let you on the plane; but if you buy a fourth, big bag, and put your original three into the fourth, everything will be fine.

Java's syntax sometimes forces this same kind of behavior. There are places (mostly inside the control structures we will study later in this reading) where only one statement is allowed; if you want multiple statements there, you must put them inside one big statement. That single, big statement is called a block-statement, of sometimes just a block. The EBNF rule for blocks is

block-statement <= {{statement}}

In this EBNF rule, the outer braces stand for themselves, the inner ones mean repetition. That is, blocks are some number of statements enclosed in braces. So, a block itself is a statement, inside which we can put other statements. Although we write this EBNF rule on one line, block statements in our code often span many lines, with each statement inside the block appearing on its own line, indented in the braces.

Semantically, Java executes a block by sequentially executing the statements that it contains (in the exact same order that they appear in the block). When giving directions to humans or computers, often the order in which the directions are followed is critical. If the directions say, "To disarm the bomb, cut the blue wire and then cut the red wire" it would not be a good idea for us to change the order in which these wires are cut.

If a statement inside a block declares a variable, that variable can be used in subsequent statements inside that block; after Java executes all the statements in a block, the block is finished executing, and ALL variables declared inside the block become undeclared. So, such variables are called local variables because they exist only locally, inside the block, while its statements are executing.

Technically, we call all those places that a variable can be used the scope of that variable. So the scope of local declarations in blocks include all subsequence statements in that block.

Blocks themselves can be nested. Any variables declared in an outer block can be used in an inner block. For example

{ //Outer block
  int x = 1;
  { //Inner block
    System.out.println(x);     //Refers to x in outer block
    x = 3;                     //Refers to x in outer block
  }
  System.out.println(x);
}
This example illustrates that the scope of the variable x includes the whole outer-block, which includes its inner block too. The inner block is just one statement that is included in the outer block (which is itself one bigger statement). In this example, Java prints 1 followed by 3.

If we had moved the declaration from the outer block to the start of the inner block, Java would detect and report an error at compile time.

{ //Outer block
  { //Inner block
    int x = 1;
    System.out.println(x);     //Refers to x in inner block
    x = 3;                     //Refers to x in inner block
  }
  System.out.println(x);       //Error! the x in the inner block
                               // is undeclared for this statement.
}
With this placement, the variable x would not be accessible outside the inner block, so it would be unknown in the final print method. The scope of x is just the inner block in which it is declared.

Putting Everything Together Finally, putting together everything that we have learned about Java, the following block contains a variety of statements that perform a simple computation: declaring variables, prompting the user for values to store in these variables (by calling a method), performing a simple calculation with these variables (storing the result in another variable with a state change operator) and displaying the result in the console window.
{        
  double gravity;           //meter/sec/sec
  double height;            //meters
  double time;              //sec
		  
  //Input
  gravity = Prompt.forDouble("Enter gravity (in m/s/s)");
  height  = Prompt.forDouble("Enter height of drop (in m)");
		  
  //Calculate
  time = Math.sqrt(2.*height/gravity);
		  
  //Output
  System.out.println();
  System.out.println("Drop time = " + time + " secs");
}
Note that by intializing variables when they are declared, we could "simplify" this code, writing it as follows. Both blocks ultimate produce the same results.
{  
  //Input      
  double gravity = Prompt.forDouble("Enter gravity (in m/s/s)");
  double height  = Prompt.forDouble("Enter height of drop (in m)");

  //Calculate	  
  double time = Math.sqrt(2.*height/gravity);
		  
  //Output
  System.out.println();
  System.out.println("Drop time = " + time + " secs");
}
In fact, there is no need in this program for variables at all! We can squeeze this entire program down to just one statement, using the \n escape sequence inside one huge (4 line) output statement.
{  
  System.out.println("\nDrop time = " + 
      Math.sqrt(2.*Prompt.forDouble("Enter height of drop (in m)")
                /Prompt.forDouble("Enter gravity (in m/s/s)")) +
      " secs");
}
Although this program is the smallest yet, it is a bit complicated to follow the calculation, which includes two prompts to the user as part of the formula being computed. Thus, while smaller is generally better, it isn't here; sometimes storing partial calculations in nicely named variables helps us to write clearer, easier to understand programs (a main goal of ICS-21). Of course, if the program needs to use the values entered by the user more than once, we should store them in variables to avoid reprompting the user for the same information multiple times.

Hand Simulating State Changes As programmers, we must be able to analyze our programs to verify that they are correct, or detect where bugs occur (the hard part) and fix them (an easier part). The most important way to analyze code is to be able to hand simulate it. The "input" to a hand simulation is
  • Variables and their current values (called the initial state)
  • A block of code (a sequence of statements)
  • If needed, values that the user enters to prompts on the console
The "output" to a hand simulation is
  • The same variables and their resulting values, and the contents of the console (called the final state)
During a hand simulation we construct a trace table of state-changes for each statement executed in the block of code; these include changes to the states of variables and changes to the state of the console (what input/output the program does).

Here is a simple example (no input/output) of such a trace table. Assume int x=5; int y=8; and the block {x=y; y=x;} If beginning students are asked to predict what the code does, the most common response is that it swaps the values in x and y. Let's see what really happens using a trace table (note that a table cell shows the value stored in a variable after the statement on its line is finished).

StatementxyConsole
Initial States58 
x=y;8  
y=x; 8 

So, we see that the values in the variables are not swapped, but that y's initial value ends up stored in both x and y. In some sense, the simplest thing to do with two variables is to exchange their values; yet the intuitive way to write code for this task is incorrect. Don't gloss over this observation, because it is very important. The kind of reasoning a programmer does about state changes in code is very different from the kind of reasoning a mathematician does about equations.

One correct way to swap the values stored in two variables is: {int temp=x; x=y; y=temp;}, and the hand simulation illustrating its correctness (using the same initial state).

StatementxytempConsole
Initial States58Undeclared 
int temp=x;  5 
x=y;8   
y=temp; 5Undeclared 

Note how temp is shown as undeclared before the block is executed and also becomes undeclared after Java finishes executing the block. But temp plays a crucial part in the computation, while Java is executing the statements in the block that it is declared in.

As a final example, let's examine the trace table for a block that does I/O too. Here there are no variables in the initial state: the block to execute is:

    {
      double x,y;
      x = Prompt.forDouble("Enter x");
      y = Math.pow(x,3.);
      System.out.println(x + " cubed = " + y);
    }
Finally, when prompted, the user will enter a 5.1 on the console.

StatementxyConsole
Initial StatesUndeclaredUndeclared 
double x,y;?? 
x = Prompt.forDouble("Enter x");5.1 Enter x: 5.1
y = Math.pow(x,3.); 132.651 
System.out.println(x + " cubed = " + y);UndeclaredUndeclared5.1 cubed = 132.651

Here the Console column shows what is on each line (on the first line, the prompt and the value that the user enters; on the second line the answer). This is certainly a lot of work for such a simple example; but if you can easily write such trace tables, you can use them to debug code that has much subtler errors.


Boxing Statements Just as we used oval diagrams to understand the structure of expressions (and their subexpressions), we will use box diagrams to understand statements (and in the case of control structures, their substatements).

Right now we know three kinds of statements: declaration statements, expression statements, and block statements. Declaration statements and expression statements contain never contain substatements; block statements contain substatements: the statements that the block executed sequentially. The example below includes multiple occurences of each of these kinds of statements. Each statement appears inside a box.

  Practice the skill of boxing statements. Learn to "see" statements inside of statements, the way a programmer does. Notice how consistent indenting in the code makes this task easier.


if Statements In Java, if statements allow us to choose whether or not to execute another statement, or which one of many statements to execute (like both the option and alternative forms in EBNF).

There are three forms of if statements in Java.

  • if
  • if/else
  • cascaded if or cascaded if/else
The general form of all if statements is given by one EBNF rule

if-statement <= if (expression) statement [else statement]

As a syntax constraint, expression must result in a boolean value (if it doesn't, the Java compiler detects and reports this error). Note that both if and else are keywords in Java, and the test expression, no matter how simple or complicated, must always appear inside parentheses. Finally, although we write this EBNF rule on one line, we write if statements in code that span at least two (and often many more) lines, and contain indented statements.

An if statement (discarding the option) decides whether or not to execute its statement. We write it as

   if (test)
     statement
Recall that statement can also be a block. Two if statement examples are
   if (x < 0)
     x = -x;


   if (myNumber == rouletteNumber) {
     myWins++;
     myPurse += stakes;
   }
Notice where the opening and closing brace appear for this block: this is the standard style that we will always use for blocks inside if statements.

Semantically, Java executes an if statement as follows

  • Evaluate the test expression.
  • If it is true, execute the (single) statement after the test.
  • If it is false, skip the (single) statement after the test.
So, if the test evaluates to true in the second example, Java executes the block statement; it executes the block by sequentially executing the two expression statements that it contains. If the test evaluates to false in the second example, Java skips the block statement (executing neither of the statements that it contains).

An if/else statement (including the option) decides which one of its two statements to execute. We write it as

   if (test)
     statement1
   else
     statement2
Recall that statement1 and/or statement2 can also be a block Two example if/else statements are

   if (x%2 == 0)     //is x is even?
     x = x/2;
   else
     x = 3*x+1;

   if (x > y) {
      min = y;
      max = x;
   }else{
      min = x;
      max = y;
   }
Semantically, Java executes an if/else statement as follows
  • Evaluate the test expression.
  • If it is true, execute statement1, which appears directly after the test, then skip statement2.
  • If it is false, skip statement1, then execute statement2, which appears directly after the keyword else.
So in if/else statements, Java always executes one of the two substatements that it controls. This is a bit different from the plain if statement, which decides whether or not to execute the one statement that it controls.

A cascaded if (or cascaded if/else) decides which one (if any) of many statements to execute. The general form of the cascade if in Java is

   if (test1)
     statement1
   else if (test2)
     statement2
   else if (test3)
     statement3
   else ...
     ...
   else if (testN)
     statementN

   or 

   if (test1)
     statement1
   else if (test2)
     statement2
   else if (test3)
     statement3
   else ...
     ...
   else if (testN)
     statementN
   else
     statementN+1
  
A cascaded if is built from many if/else statements, where each of statements in the else part is another if/else statement (except possibly the lst one). An example cascaded if statement, assuming the declaration char grade; is
   if (testScore >= 90)
     grade = 'A';
   else if (testScore >= 80)
     grade = 'B';
   else if (testScore >= 70)
     grade = 'C';
   else if (testScore >= 60)
     grade = 'D';
   else
     grade = 'F';
Semantically, Java executes a cascaded if statement as follows
  • Evaluate the first test expression.
  • If it is true, execute the statement after the test and terminate the cascaded if (don't check any more tests or execute any more statements).
  • If it is false, evaluate the second test expression.
  • If it is true, execute the statement after the test and terminate the cascaded if (don't check any more tests or execute any more statements).
  • Continue following rules of this form until a true test is found, or the last test is evaluated.
  • If the last test is true, execute the statement after the test and terminate the cascaded if (there are no more tests to check).
  • If the last test is false, and it is an if statement, terminate the cascaded if; if it is an if/else statement, execute the statement after the else keyword.
So in the cascaded if, exactly one statement -the one after after the first true test is executed; if no tests are true, either no statements are executed (when the last statement is an if) or the statement after the last else is executed. Graphically, we can summarize the control flow in the three kinds of ifs as


Hand Simulating if statements We can extend our use of trace tables to hand simulations of if statements. We include a special Explanation column to indicate the result of evaluating test and which statement Java executes next. Let's write two trace tables for hand simulating the first if statement shown above.

StatementxConsoleExplanation
Initial State-5  
if (x < 0)  true: execute next statement
x = -x;5 if finished

StatementxConsoleExplanation
Initial State5  
if (x < 0)  false: skip next statement; if finished

Next, let's write two trace tables for hand simulating the second if/else statement shown above.

StatementxyminmaxConsoleExplanation
Initial State53??  
if (x > y)     true: execute next statement
min = y;  3  1st statement in block
max = x;   5 2nd statement in block; block and if finished

StatementxyminmaxConsoleExplanation
Initial State35??  
if (x > y)     false: execute statement after else
min = x;  3  1st statement in block
max = y;   5 2nd statement in block; block and if finished

What is the trace table for this example if the values stored in x and y are equal? Does it produce the correct result? Can you change the test to >= and still always get the same result? Can there be two different ways of getting the same result?

Finally, let's write a trace table for hand simulating the cascaded if statement shown above.

StatementtestScoregradeConsoleExplanation
Initial State73?  
if (testScore >= 90)   false: execute if in else
if (testScore >= 80)   false: execute if in else
if (testScore >= 70)   true: execute next statement
grade = 'C'; 'C' cascaded if finished


A Clock Example Let's take a quick look at an interesting task that combines all the statements that we have studied. Assume that we have declared the following variables for a "military" style clock: e.g., 00:00 represents midnight, 9:03 represents 9:03am, 14:23 represents 2:23 pm, and 23:59 represents 11:59pm.
    int minute; //in the range [0,59] inclusive
    int hour;   //in the range [0..23] inclusive
Also assume that the method emitBeeps takes a single int operand and emits that many beeps. Finally, assume that the following code is called once a minute by the operating system; when we study Java threads we will learn how to arrange for such an action to occur repeatedly;.
    if (minute != 59)
      minute++;
    else {
      emitBeeps(hour+1);
      minute = 0;
      if (hour != 23)
        hour++;
      else
        hour = 0;
    }
Each time the code is called, it advances minute (and hour, if necssary) ensuring they store only legal values; on the hour, the code beeps that many times (once at 1 am, twice at 2am, ... 12 times at noon, 13 times at 1pm, ..., and 24 times at midnight). Let's write two trace tables for hand simulating this code in two different initial situations: first at 10:15 (10:15am).

StatementhourminuteConsoleExplanation
Initial State1015  
if (minute != 59)   true: execute next statement
minute++; 16 if/else finished

Here, the minute is incremented by 1, and nothing else happens.

Now lets write a trace table for the initial situation 22:59 (10:59pm).

StatementhourminuteConsoleExplanation
Initial State2259  
if (minute != 59)   false: execute statement after else
emitBeeps(hour+1);  Beep 23 times1st statement in block
minute = 0; 0 2nd statement in block
if (hour != 23)   true: execute next statement
hour++;23  inner if/else finished, and outer if/else finished

Here, much more happens: the clock beeps 23 times (for 11:00pm) and the minute is reset to 0 while the hour advances to 23.


A Caution: = vs ==
in ifs
Imagine that you want to write code that doubles the value stored in an int variable x, but only if it stores 10. The following if statement proposes to solve the problem
   if (x = 10)
     x = 2*x;
Carefully examine the test, it is written as x = 10 not x == 10. Did you see that the first time you read it? Most students don't.

It is a common mistake for programmers to write = accidentally instead of == in if tests. But the good news is that the Java compiler will detect and report a syntax constraint error, because the result type of the test is not boolean.

There are situations, though, where the Java compiler will not detect such a mistake will not be detected: when the expression itself is of type boolean. The code on the left uses = and the one on the right uses ==.

  boolean doIt = ...;                    boolean doIt = ...;
  if (doIt == true)                        if (doIt = true)
    System.out.println("Yes");               System.out.println("Yes");
Assume in both cases the the ... code evalutes to false. The left test evaluates to false, so it does not print the message. But the right test stores true into doIt (wiping out the value computed before the if) AND evaluates to true (by the semantics of the = operator), so it does print the message. The Java compiler does not report any error, because the type of the expression in both cases is boolean.

This brings us to a style point: writing == true or == false in an if's test is unnecessary, and is prone to error. For any boolean expression e, we can write just e (instead of e == true) and we can write !e (instead of e == false). Avoiding the true and false literals here is the sign of a mature programmer.

Finally, as we have already seen, if you accidentally write the expression statement x == 0; the Java compiler will detect and report a syntax constraint error, because the last operator applied in this expression statement is not a state-change operator. Older languages (C and C++) allow these expression statements, and cause programmers no end of debugging problems, so Java disallowed them, instead forcing them to be reported during compilation.


Dangling else Examine the following two if statements
   if (test1)             if (test1)
     if (test2)             if (test2)
       statement1             statement1
     else                else
       statement2           statement2
The left code looks like it has an if/else inside an if. The right code looks like it has an if inside and if/else. But what we see is based on whitespace/indentation. What Java sees for both is EXACTLY THE SAME TOKENS: if (test1) if (test2) statement1 else statement2 because whitespace is removed once Java tokenizes a program.

So, Java interprets both code fragments in exactly the same way! Which interpretation does Java use for these tokens? We need an extra syntax rule that helps us gree on which interpretion is the correct one: an else belongs to the most recently seen if that it can belong to. So, Java uses the left interpretation.

To force the other interpretation, matching the else with the first if, we must use a block, and write

   if (test1) {
     if (test2)
       statement1
   }else                 
     statement2
Now the else (which is outside the block) cannot possibly belong to the if that is inside the block, because all parts of that if statement must reside entirely in the block. So the final else now belongs with the first if.

This is called th dangling else problem, and it is hard for programmers to see. We must carefully indent our if statements accurately, to reflect which elses belong with which if, otherwise our program will contain a subtle error that is very hard for us to locate. In fact, some programmers advocate ALWAYS using block in if/else statements to avoid dangling elses. The disadvantage of this approach is that in simple cases, the extra blocks create code that is harder to read. We will discuss style principles in more detail later in the quarter.


if Pragmatics When writing decisions, determine the correct form: if, if/else, or cascaded if. If you are unsure about which one is correct, try the simpler forms first.

Indent the parts of the if and the statements that it contains to illustrate the logical structure of the if; when blocks are used, place the braces in the positions shown in the examples above. Ensure that the indentation (making the code easier for humans to read) accurately reflects how Java reads the tokens (e.g., beware of a dangling else).

The key to understanding an if statement is understanding its test(s). Ensure that for some values of its variables, every test can evaluate to both true and false (otherwise the test is probably wrong). For example, what is wrong with the following code?

Study it carefully and hand simulate it for a few different values of x.

  if (x > 2 || x < 5)
    x++;
Is this test really the right one? probably not: no int value stored in x makes the test false; try to find one. If it were correctly, we could simplify this code by removing the whole if statement, simplifying it to just x++, which always performs this action.

for Statements In Java, for statements allow us to repeatedly execute another statement.

To begin, we present a simple, useful, legal, but incomplete, form for the for statement. The EBNF rule for this simplified for statement is

for-statement <= for(;;) statement

We call statement the body of the for loop. Although we write this EBNF rule on one line, we write for statements in code that span at least two (and often many more) lines. Finally, note that for is a keyword in Java.

The most typical form of the for statement is

   for (;;) {
     statements (i.e. a sequence of statements inside a block)
   }
Here the body of the for is a block.

Semantically, Java executes the for statement by executing its body over and over again. Thus, when done executing the body, Java "loops back" and re-executes it. That is why we often refer to such a statement as a "for loop". Such a loop runs forever; or, more accurately, until Java executes a break statement inside the loop (discussed in the next section) forces Java to terminate the loop. Two example for loops (each infinite) are

   for (;;)
     System.out.println("You're Great!");


   int count = 0;
   for (;;) {
     System.out.println(count);
     count++;
   }
The first example fills the screen with Your're Great!. The second example starts by displaying the value 0, then 1, then 2, then 3, etc. with the next value displayed becoming larger by one for each iteration.

Let's hand simulate this second example and write a trace table for it.

StatementcountConsoleExplanation
Initial StateUndeclared  
int count = 0;0  
for (;;) {  execute body first time
System.out.println(count); 01st statement in block
count++;1 last statement in block
for (;;) {  execute body again
System.out.println(count); 0
1
1st statement in block
count++;2 last statement in block
for (;;) {  execute body again
System.out.println(count); 0
1
2
1st statement in block
count++;3 last statement in block
............

Of course, this process continues endlessly, so we cannot show a complete trace table for this code or any other infinite loop. You can always terminate a program in Eclipse by using by pressing the red square in the console window, if you suspect your program is in an infinite loop and you want to stop it.

In the next section, we will explain how the break statement allows the program itself to terminate the loop.


break Statements The EBNF rule for the break statement is very simple

break-statement <= break;

Java imposes a syntax constraint that a break statement must appear inside the body of some loop. Finally, note that break is another keyword in Java.

In real programs, break statements appear inside if statements (which themselves are inside the bodies of loops), so a typical example is

   if (count == 0)
     break;
Semantically, whenever a break statement is executed, Java terminates the inner-most loop that it apears in; it breaks out of that loop. Terminating a loop means Java next executes the statement AFTER the body of the loop (it does not mean that the program terminates). By putting a break statement inside an if statement, the if can control (based on its test) whether or not the break statement is executed: the test determines whether or not the loop terminates on this iteration.

A tyical combination of for and break statements is

   int countdown = 3;
   for(;;) {
     System.out.print(countdown + "...");
     if (countdown == 0)
       break;
     countdown--;
   }
   System.out.println("Blastoff");
Let's hand simulate this example and write a trace table for it. We call such a for/break combination a count down loop.

StatementcountdownConsoleExplanation
Initial StateUndeclared  
int countdown = 3;3  
for(;;) {   execute body first time
System.out.print(countdown + "..."); 3...1st statement in block
if (countdown == 0)  false skip next (break;) statement; if finished
countdown--;2 last statement in block
for(;;) {   execute body again
System.out.print(countdown + "..."); 3...2...1st statement in block
if (countdown == 0)  false skip next (break;) statement; if finished
countdown--;1 last statement in block
for(;;) {   execute body again
System.out.print(countdown + "..."); 3...2...1...1st statement in block
if (countdown == 0)  false skip next (break;) statement; if finished
countdown--;0 last statement in block
for(;;) {   execute body again
System.out.print(countdown + "..."); 3...2...1...0...1st statement in block
if (countdown == 0)  true execute next (break;) statement;
break;  terminate for loop
System.out.println("Blastoff"); 3...2...1...0...Blastoff1st statement AFTER loop body
  We can graphically summarize the control flow in cooperating for and break statements as


More for/break Examples Let's look at two more interesting kinds of loops that combine for and break. The first is called a count up loop: the variable x counts up to the value stored in the variable max. Notice that max is declared and initialized by the value entered by the user.
    int max = Prompt.forInt("Enter Number to Sum To");
    int x   = 0;       //Stores value to add to sum
    int sum = 0;       //Stores sum that x is added to

    for (;;) {
      if (x == max)
        break;
      x++;
      sum += x;
    }
    System.out.println("1+2+...+" + max + " = " + sum);
Assuming the user enters 5 when prompted, let's hand simulate these statements and write a trace table for it.

StatementmaxxsumConsoleExplanation
Initial StateUndeclaredUndeclaredUndeclared  
int max = Prompt(...);5  Enter ...: 5  
int x = 0; 0   
int sum = 0;  0  
for (;;) {    execute body first time
if (x == max)    false skip next (break;) statement; if finished
x++; 1   
sum+= x;  1 last statement in block
for (;;) {    execute body again
if (x == max)    false skip next (break;) statement; if finished
x++; 2   
sum+= x;  3 last statement in block
for (;;) {    execute body again
if (x == max)    false skip next (break;) statement; if finished
x++; 3   
sum+= x;  6 last statement in block
for (;;) {    execute body again
if (x == max)    false skip next (break;) statement; if finished
x++; 4   
sum+= x;  10 last statement in block
for (;;) {    execute body again
if (x == max)    false skip next (break;) statement; if finished
x++; 5   
sum+= x;  15 last statement in block
for (;;) {    execute body again
if (x == max)    true execute next (break;) statement
break;    terminate for loop
System.out.println(...);   Enter ...:5
1+2+...+5 = 15
1st statement AFTER loop body
  The second example is called a sentinel terminated loop. Here the user enters a special value (called a sentinel) to inform the program that there are no more values to input. The sentinel is not processed by the normal code in the loop body
    int count = 0;
    int sum   = 0;
    for (;;) {
      int score = Prompt.forInt("Enter a Score (-1 to Terminate)");
      if (score == -1)
        break;
      count++;
      sum += score;
    }
    System.out.println("Average = " + (double)sum/(double)count);
Assuming the user enters the value 3, 6, 4, and the sentinel -1 respectively when prompted, let's hand simulate these statements and write a trace table for it.
 
StatementcountsumscoreConsoleExplanation
Initial StateUndeclaredUndeclaredUndeclared  
int count = 0;0    
int sum = 0; 0   
for (;;) {    execute body first time
int score = Prompt.forInt(...);  3Enter ...: 3 
if (score == -1)    false skip next (break;) statement; if finished
count++;1    
sum+= score; 3Undeclared last statement in block
for (;;) {    execute body again
int score = Prompt.forInt(...);  6Enter ...: 3
Enter ...: 6
 
if (score == -1)    false skip next (break;) statement; if finished
count++;2    
sum+= score; 9Undeclared last statement in block
for (;;) {    execute body again
int score = Prompt.forInt(...);  4Enter ...: 3
Enter ...: 6
Enter ...: 4
 
if (score == -1)    false skip next (break;) statement; if finished
count++;3    
sum+= score; 13Undeclared last statement in block
for (;;) {    execute body again
int score = Prompt.forInt(...);  -1Enter ...: 3
Enter ...: 6
Enter ...: 4
Enter ...: -1
 
if (score == -1)    true execute next (break;) statement
break;  Undeclared terminate for loop
System.out.println(...)  Average = 4.33333331st statement AFTER loop body
 

Notice that each time that the for loop executes its body, it declares, intializes, then undeclares its local variable score. This variable could be declared and left uninitialized before the loop, as int score; and then appear in the loop as just score = Prompt.forInt("Enter a Score (-1 to Terminate)");. But because the values stored in this variable are never (and should never be)used outside the loop, we have chosen to not even declare this variable outside the loop. The fact that this variable is declared/undeclared many times does not affect the correctness (nor the speed) of this code.


Compact Trace Tables When we hand simulate programs with complicated control structures, most of the trace table is occupied by information relating to control structures deciding which statements to execute next, as opposed to statements that actually change the state of variables or the console window. Such trace tables are cumbersome to create and hard to read.

Compact trace tables remove all the information related to control structures, and instead focus on state changes to variables and the console window. To construct a compact trace table, we list all variables and Console in separate columns (and omit Explanation). Only when the code changes the state of a variable OR the console window do we update information in the appropriate column; and we always do so right beneath the last entry for this column.

Note that what we lose in a compact trace table (we gain conciseness) is an indication of the order in which different variables have their state changed: because each column is shown as compactly as possible (no blank entries); there is no correlation among columns whose states changed. Here are compact trace tables for the three standard trace tables shown above. First, the count down loop.

countdownConsole
Undeclared(blank)
33...
23...2...
13...2...1...
03...2...1...0...
 3...2...1...0...Blastoff

Next, the count up loop.

maxxsumConsole
UndeclaredUndeclaredUndeclared(blank)
500Enter Number to Sum To: 5
 111+2+...+5 = 15
 23 
 36 
 410 
 515 

Finally, the sentinel terminated loop.

countsumscoreConsole
UndeclaredUndeclaredUndeclared(blank)
003Enter a Score (-1 to Terminate): 3
136Enter a Score (-1 to Terminate): 6
294Enter a Score (-1 to Terminate): 4
313-1Enter a Score (-1 to Terminate): -1
   Average = 4.333333

Remember, in a compact trace table, all the blank entries are at the bottom of a column. A column entry is filled in only when all the column entries on top of it have been filled in.


General for/break; while/do We can write ANY looping code using the for and break that we know; but, there is a more general form of the for statement that allows use to write many loops more compactly and clearly. When we study arrays, iterators, and self referential objects, these forms will become more and more useful.

The general for statement packages all the information needed for a count-down or count-up loop into one locality, making it easier to read, write, and understand. The EBNF rule for the general for statement is

expression-list <= expression{,expression}
for-init             <= type variable-declarators | expression-list
for-update       <= expression-list
for-statement  <= [identifier:] for([for-init];[expression];[for-update]) statement

Note that if we discard all the options, we are back at the for(;;) statement that we have studied. As a syntax constraint, if the expression option (in the middle of the semi-colons) is included, its resulting type must boolean: this part is called the continuation test. Also, each expression in an expression-list must adhere to the same constraints as an expression-statement: it must apply a state-change operator or a method call last in the expression.

For example, we can use such a for statements to simplify the code that sums all the integers up to max.

  int max = Prompt.forInt("Enter Number to Sum To)";
  int sum = 0;       //Holds Sum of 1..x

  for (int x=1; x<=max; x++)
    sum += x;
  System.out.println("1+2+...+" + max + " = " + sum);
The for loop is so powerful that it reduces the body of the loop to a single expression statement.

Semantically the for loop executes as follows.

  1. Execute the code specified in for-init (in the example, the declaration int x=1: if for-init is a declaration, the scope of the variable(s) declared is the for statement; any disappear when the loop terminates.
  2. Evaluate the test as specified in the boolean expression (in this example x <= max): if true execute the body of the for loop; if false terminate the for loop. If the expression is omitted, execute the body of the for loop.
  3. After executing the entire body of the for, evaluate for-update (in the example x++: note that the last operator applied here is a stat-change operator).
  4. Continue back at step 2 (in the example, checking the continuation test for the new value stored in x).

Pictorially, the semantics look like

What makes the for loop so powerful is the way it groups together, in one locality, all the information that controls the loop. Here is a standard (not compact) trace table illustrating these semantics in the code above.

StatementmaxxsumConsoleExplanation
Initial StateUndeclaredUndeclaredUndeclared  
int max = ...;5  Enter ...: 5  
int sum = 0;  0  
for (int x=1; x<=max; x++) 1  Initialize; test is true execute body
sum+= x;  1 last statement in body
for (int x=1; x<=max; x++) 2  increment; test is true execute body
sum+= x;  3 last statement in body
for (int x=1; x<=max; x++) 3  increment; test is true execute body
sum+= x;  6 last statement in body
for (int x=1; x<=max; x++) 4  increment; test is true execute body
sum+= x;  10 last statement in body
for (int x=1; x<=max; x++) 5  increment; test is true execute body
sum+= x;  15 last statement in body
for (int x=1; x<=max; x++) Undeclared  increment; test is false terminate loop
System.out.println(...);   Enter ...:5
1+2+...+5 = 15
1st statement AFTER loop body

Note that the variable x becomes undeclared after the for statement terminates. Thus, we cannot refer to it after the for loop's body (where we print the statistics); if we did write its name there, the Java compiler would detect and report an error. If we did want to refer to this value AFTER the for statement finishes, we could write

  int max = Prompt.forInt("Enter Number to Sum To)";
  int sum = 0;       //Holds Sum of 1..x

  int x = 1;
  for (; x<=max; x++)
    sum += x;
  System.out.println("1+2+...+" + max + " = " + sum + " and x = " + x);
Here, x is declared before the for statement, not in it, so it remains declared after the for statement finishes. Note that in this for statement, nothing appears before the first semi-colon.

Finally, a for statement can be named by an identifier (see the first option in the EBNF). Likewise, there is a more general break statement whose EBNF is

break-statement <= break [identifier] ;

In a simple break statement, Java terminates the inner-most loop that the break apears in. If a general break statement includes this option, it has a syntax constraint that it must appear inside a loop named by that same indentifier, and it terminanes that loop. This feature is only useful for loops inside loops, and even then it is very very rarely needed.


for,while,do Semantics We will now show the EBNF rule for while and do statements and explain their semantics, and the general for statement, by using the simple for and break statements. The EBNF for while and do statements is

while-statement <= while(expression) statement
do-statement     <= do statement while (expression);

Semantically, we can mechanically translate any general for loop, while loop, or do loop into an equivalent simple for(;;) loop. The Java compiler performs just this kind of transformation when it generates the machine instructions corresponding to these kinds of loops.

  for (init; continue; update)       {
    statement                          init;
                                       for (;;) {
                                         if ( !(continue) )
                                           break;
                                         statement
                                         update
                                       }
                                      }


   while (continue)                   for(;;) {
     statement                          if ( !(continue) )
                                          break;
                                        statement
                                      }
                        or even

                                     for(;continue;)
                                       statement

   do                                 for(;;) {
     statement                          statement
   while (continue)                     if ( !(continue) )
                                          break;
                                      }
For the general for statement example above, this means
  for (int x=1; x<=max; x++)      {
    sum += x;                       int x=1;
                                    for (;;) {
                                      if (!(x<=max))
                                        break
                                      sum += x;
                                      x++;
                                    }
                                  }
Note that the value of x is declared inside a special block (the outermost one); as described above, this variable disappears when the for statement (the outermost block shown above in the translation) terminates.

The while and do statements and just variants where the continuation condition is always tested first or last in the loop's body. In the case of the do loop, the body is always executed once. Pragmatically, you will see many more while loops than do loops.

Finally, most students have two problems understanding general for loops.

  • They don't realize when the update is done: it is done AFTER the loop body, right BEFORE continue is retested. Also note that x++ and x=x+1 are valid expression statements that can be used in update (but x+1 is not, because it contains no state-change operator)
  • They try to use the variable declared in init outside the loop, after it has terminated: this variable can be used only inside the loop.

for/break Pragmatics The following rules can help you synthesize and analyze (to fix bugs) loops. When designing iterative code, think in terms of the simple for and break statements and determine:
  • What statements belong before the loop (initialization code)
  • What statements belong inside the body of the loop
  • What condition terminates the loop
  • Where that condition should be tested inside the loop
Each iteration should allow some progress towards the termination condition of the loop (making the if test true). Sometimes it is easier to write the body of the loop first, and then determine what initialization is necessary before the loop.

Sylistically, write loops as shown, with block braces as shown and the body of the loop slightly indented (typically 2 spaces).

When hand simulating loops, pay special attention to the first few and last few iterations (certain kinds of errors occur only at the beginning or ending of a loop). Ensure that all the variables are properly initialized before they are examined in expressions (the Java compiler will help you here). Errors due to incorrect initialization are easy to spot if we carefully hand simulate the first iteration of a loop (and these are among the most frequent category of errors).

The break statement is the most important statement inside a loop. Clearly mark break statements using white space and/or special comments (e.g., a comment sandwich). Ensure that for all possible initial states of its variables, a loop eventually terminates (the test in the if statement containing the break will always eventually evaluate to true).

Most loops, even in industrial code, need one if/break combination. A loop to solve a very complicated problem may require multiple if/break combinations, but they can often be localized (grouped together). Only a loop that solves the most complicated kind of problems may require multiple if/break combinations distributed throughout the loop's body.

It is the mark of a good programmer to write simple loops with simple terminations. The most frequent occuring location for the if/break is the first statement in the body of the for loop (try it there first, move it elsewhere if necessary). Such a for/break combination can also be written as a while loop.

Finally, use the general form of the for statement to its maximum advantage, to clarify your loops. You may use while and do loops, but the extra thought that goes into considering them, and the fact that it is often harder to think of a "continuation" condition rather than a "termination" condition, means that I use them infrequently, preferring the for/break combination.


Boxing if, for, and break Statements Continuing our analysis of boxing statements, we illustrate below how to box if, for and break statements (as well as expression and block statements). Notice that EVERYTHING that can be syntactically considered to be a statement is in its own box. This includes declarations statements (there are none here), expression statements, blocks, entire if statements, break statements, and entire for statements.

  General for loops are boxed in a similar manner; none of the information within their parentheses are considered statements.


try-catch The EBNF of the try-catch statement is the most complex of any control structure that we have seen so far: that is a tipoff that programming in Java with exception handling is interesting. In fact, many Java courses don't cover exceptions and exception handling until much later in the quarter. But, I think that the core concepts can be demonstrated early, can be used to good advantage in stereotypical ways that are easy to understand, and can be returned to repeatedly in more complicated contexts (a spiral approach to learning). The general form of a try-catch statement is

parameter               <= type identifier
catch-clause            <= catch (parameter) block-statement
try-catch-statement <= try block-statement {catch-clause}[finally block-statement]

Although we write the try-catch-statement EBNF rule on one line, try-catch statements written in our code often span many lines (they contain mandatory blocks, which can contain many statements on different lines). The names of exceptions are actually special reference types. Although this is getting a bit ahead of ourselves, we will use the following reference types/exception names: ArithmeticException, NumberFormatException, IllegalArgumentException, IllegalStateException, IOException, and Exception (a generic name that includes all the others).

As a syntax constraint, the right hand side of each try-catch-statement must have at least one catch-clause or one finally; there can be many of the former, and a combination of both, but we cannot take 0 repetitions of catch-clause and at the same time discard the finally block. We could actually encode this restriction in EBNF, but it would make a complicated description look even more complex.

The semantics of this statement, as you might expect, are complicated as well. Java starts a try-catch by sequentially executing the statements in the block-statement immediately following the keyword try (known as the try block), just as it would execute any block. One of two things happen.

  • If no statement in the try block throws an exception, after the last statement in the block is executed, Java executes the finally block (if this option was included). Java is now done with the try-catch statement and executes the next statement following it.

  • If some statement in the try block throws an exception, the rest of the statements in that block are skipped. Java tries to find a catch-clause whose parameter matches the name of the exception that was thrown; the generic name Exception matches all exception names.
    • If the name is found, Java executes its associate block, and then Java executes the finally block (if this option was included). Java is now done with the try-catch statement and executes the next statement following it.
    • If the name is NOT found, Java executes the finally block (if this option was included). Java then skips code while it looks for another, more outer try-catch statement: one whose catch-clause does name the exception.
      • If one is found, follow the rule above. Note that it DOES NOT return to the original, inner try-catch statement.
      • If one is NOT found, Java terminates the programs, citing an uncaught exception. Typically, this results in a trace: it prints on the console the name of the uncaught exception and what methods were active when the exception was thrown.

A Simple Example In this section we will present a simplified example: it is not useful in real programs, but is useful only to illustrate the semantics of try-catch statements. Recall that the / operator throws an exception named ArithmeticException if its second operand is zero. Let us examine the effect of placing the following try-catch statement in a program.
  int percentage;
  ...some code
  try {
    int attended = Prompt.forInt("Enter attendance");
    int capacity = Prompt.forInt("Enter capacity");
    percentage = 100*attended/capacity;
    System.out.Println("percentage computed ok");
  }
  catch (ArithmeticException a) {
    System.out.println("capacity was 0; I'll assume percentage was 100%");
    percentage = 100;
  }
  ... more code
Here, if the division succeeds, percentage is set correctly, the "ok" message is printed, the try block finishes normally, and execution continues afterward, where it says ...more code (because there is no finally block).

On the other hand, if the division fails (throwing ArithmeticException), percentage is not set (the = operator is never evaluted; it requires the result from the division which we have just seen has thrown an exception) the "ok" message is skipped as Java locates the appropriate catch-clause; both statements in the catch-clause block are executed, then the try block finishes, and execution again continues afterward, where it says ...more code (because there is no finally block).

If we replaced ArithmeticException by Exception then the code would execute identically, because Exception matches all raised exceptions. If we replaced ArithmeticException by any other name, say IOException then Java would not find a matching exception; assuming that there is no outer try-catch statement to catch this exception, Java would terminate the program and print a trace on the console.

One reason why this example is not realistic is that we can easily check whether the division will fail with an if statement and avoid the need for a try-catch statement all together.

  int percentage;
  ...some code
  int attended = Prompt.forInt("Enter attendance");
  int capacity = Prompt.forInt("Enter capacity");
  if (capacity != 0) {
    percentage = 100*attended/capacity;
    System.out.Println("percentage computed ok");
  }else {
    System.out.println("capacity was 0; I'll assume percentage was 100%");
    percentage = 100;
  }
  ... more code
In the following two examples, which are much more realistic, we will need a try-catch statement to solve the problem: we cannot use an if statement to check whether an exception is about to be thrown.

Prompting with try-catch In this section we will present a more realistic example. In fact, similar code appears inside the Prompt.forInt method. Understanding how this code works requires a mastery of the semantics of many Java statements. First, we must know that the Integer.parseInt method (from the Java library) has the following prototype
int Integer.parseInt(String) throws NumberFormatException
This methods takes a String as an argument. If that argument represents the value of a legal integer, it returns that value as an int; if it does not represent a legal integer, it cannot return any reasonable value, so it throws NumberFormatException.

Thus Integer.parseInt("-10") returns the int -10 and Integer.parseInt("-1x0") throws NumberFormatException. There is no method that Java provides to check whether Integer.parseInt will throw an exception: we have to call that method to see what it does.

Now, let us see how the following code, a combination of a for loop, break statement (not in an if!) and try-catch, prompts the usre until he/she enters a valid integer, whose value is stored into answer

  int answer;
  for(;;)
    try {
      answer = Integer.parseInt(Prompt.forString("Enter integer"));
      break;
    }
    catch (NumberFormatException e) {
      System.out.println("Error: please enter a valid integer");
    }
  ...process answer
Here, the for loop repeatedly executes the try-catch statement. First, let us see what happens if the user enters a valid integer. During the first iteration of the loop, Java executes the first statement; the user enters a valid integer (read as a String that is passed to the Integer.parseInt method); so, this method does not throw an exception, but instead returns a result that is stored in answer. Thus, the second statement in the block is reached; this break statement terminates the entire for loop, and execution continue after the for loop, where it says ...process answer.

Now, let us see what happens if the user enters an INVALID integer. During the first iteration of the loop, Java executes the first statement; the user enters an invalid integer (read as a String that is passed to the Integer.parseInt method); so, this method throws a NumberFormatException. Java skips the break statement and instead finds the catch matching the exception; its following block prints an error message. Now the try-catch statement is finished; but this statement is the body of a for loop, so it is executed again!

Therefore, this loop will continue executing so long as the user enters an invalid integer; the first time that the user enters a valid integer (see the description above) its value will be stored into answer and the break statement following it will be executed to terminate the loop.

So generally, we have designed code that (potentially) repeatedly performs some operation until an exception is NOT thrown. In the next section, we will design code that repeatedly performs some operation until an exception IS thrown: e.g., we are anticipating that an exception will eventually terminate the loop, which continues executing until it is does. Together, these two forms occur frequently in exception handing code.


Reading Files with try-catch In this section we will present another realistic example. In fact, code similar to this will be present in most programs that read files. First, we must learn that the readInt method (from a Java library class that I have written) has the following prototype
int readInt() throws NumberFormatException, EndOfFileException
This method skips any white space in a file and returns the next integer value that it contains (if it succeeds). There are two ways for it to fail, each denoted by a different exception name.
  1. There is a value in the file, but it is not an integer; in this case the method throws NumberFormatException.
  2. There are no more values (of any type) in the file; in this case the method throws EndOfFileException.
The following code assumes the variable inputFile refer to an object representing a file (we'll learn more about this in a later lecture). It reads every value in a file, accumulating the sum, and ultimately printing it.
  int sum = 0;
  for (;;)
    try {
      int aValue = inputFile.readInt();
      sum += aValue;
    }
    catch (EndOfFileException eofe) {
     break;
    }
  System.out.println("Sum = " + sum);
Let us see what happens if the file contains two integers.
  1. Java starts to execute the for loop, which contains just one statement: a try block, During this first iteration of the loop, Java executes the first statement in the try. block; calling the readInt method reads a valid integer from the file and stores into aValue. In the next statement the sum is incremented by this value. The try block is finished, and the for loop executes it a second time.
  2. During the second iteration of the loop, Java executes the first statement in the try. block; calling the readInt method reads another valid integer from the file stores into aValue. In the next statement the sum is incremented by this new value. The try block is finished, and the for loop executes it a third time.
  3. During the third iteration of the loop, Java executes the first statement in the try. block; calling the readInt methods causes it to throw EndOfFileException, because there are no more values in the file to read. This error is caught by the catch (EndOfFileException eofe) clause, whose block contains a break statement that terminates the for loop. Java continues by executing the statement after the for loop, printing the accumulated sum.

This code repeatedly performs some operation until an exception is thrown. Note that if a non-integer value appears in the file, then calling the readInt method causes it to throw NumberFormatException. This exception is not caught by the try-catch shown above, so Java terminates the program and prints a trace on the console.

Finally, because the only place that aValue is used is to add to sum, we can simplify this code a bit and write.

  int sum = 0;
  for (;;)
    try {
      sum += inputFile.readInt();
    }
    catch (EndOfFileException eofe) {
      break;
    }
  System.out.println("Sum = " + sum);

Problem Set To ensure that you understand all the material in this lecture, please solve the the announced problems after you read the lecture.

If you get stumped on any problem, go back and read the relevant part of the lecture. If you still have questions, please get help from the Instructor, a CA, or any other student.

  1. Explain which of the following state-change expressions are legal and illegal. Assume we have declared int a,b,c;.
       a + b = c;
       a = 2++;
       (a = b) = c;
       a = Prompt.forInt("Enter a") + b;
       a = System.out.println("Success!");

  2. In each of the following expression statements, carefully apply your knowledge of syntax (tokens, operator precedence, and associativity) and semantics to explain (a) What values are stored in all variables whose states are changed. (b) What is the result computed by the expression. (c) What oval diagram illustrates parts a and b. Assume that each statement is executed just after the declaration int a=3, b=5, c=8;
       a = b++ + c;
       a = b + ++c;
       c = b+++c++;
       a+=b+=c+=1;
       a = b = c + 1;
       a = (b = c) + 1;
       System.out.println("" + ++b + b);
       System.out.println("" + b++ + b);

  3. Explain what is displayed on the console for the top and bottom block. Assume we have declared int i; char c; and that the ASCII equivalent of 'A' is 65.
       {i = 'A';  System.out.println(i);}
       {c =  65;  System.out.println(c);}

  4. Show what is displayed in the console window after Java executes the following expression statements. Be very careful to show which word occurs on which lines. Remember that the escape character \n starts a new line.
       System.out.print("When " + "in ");
       System.out.println( "the "
            + "course "
            + "of ");
       System.out.print("human\nevents ");
       System.out.print("it ");
       System.out.print("becomes ");
       System.out.println("necessary ");
       System.out.print("for one nation to sever...");

  5. Examine the following 6 blocks (all are are permutations of the same three statements). Classify each block as syntactically legal or illegal (hint: certain statement orderings are illegal because they violate a constraint on where a variable declared inside a block can be used). For those blocks that are legal, determine whether or not they swap of the values stored in x and y.
            {int temp=x; x=y;        y=temp;}
            {int temp=x; y=temp;     x=y;}
            {x=y;        int temp=x; y=temp;}
            {x=y;        y=temp;     int temp=x;}
            {y=temp;     x=y;        int temp=x;}
            {y=temp;     int temp=x; x=y;}

  6. Examine each of the following statements; determine whether it is true or false (and be prepared to support your answer or show a counter example).
    • A block can be empty (contain no statements).
    • A block can be unitary (contain 1 statement).
    • The statements x=y; and y=x; have identical meanings.
    • If we declare int x=5,y=8; and Java executes the expression statement x=y; immediately followed by y=3; then both x and y now store 3.
    • A single statement can change the state of more than one variable.

  7. The following block is rejected at compile time by the Java compiler. Write the error message that Java reports. Explain why it makes sense to recognize this problem and report the error.
      {
        int a;
        System.out.println("a = " + a);
      }

  8. Which of the following is easier to understand: the single statement or double statement? Verify that both perform the same computation.
      myPurse += stakes + 0*(myWins++);          myWins++;
                                                 myPurse += stakes;

  9. Assume that we declare a char grade; and guarantee that it stores a letter corresponding to a UCI grade: 'A', 'B', 'C', 'D', or 'F'. Write an if statement that computes the number of quality points for that grade and stores it in int qp; an A is worth 4, a B is worth 3, a C is worth 2, a D is worth 1, and an R is worth 0.

  10. Assume that we declare int hours; Write an if statement that computes the pay (in cents) due a worker according the following formulas: 625*Hours if the hours worked is less than or equal to 40; 625*Hours + 725*(Hours-40) if the hours worked is greather than 40. Store the result in int centsPay; Try a few examples under, at, and over 40 hours to verify your statement is correct.

  11. Assume that we declare int x, y; boolean isIt; Write a trace table for the hand simulation of the following Java statements: one where x stores 3 and y stores 5; and another where x stores 5 and y stores 3. State whether the results are the same or different in each case.
       if (x < y )            if (x < y)
         isIt = true;          isIt = true;
       else                  isIt = false;
         isIt = false;
    Which statement side is equivalent to the expression statement isIt = (x < y);

  12. Assume that we declare int studentAnswer, correctAnswer, wrongCount; Explain what is wrong with the following statement (there is a syntax error).
       if (studentAnswer == correctAnswer)
       else
         wrongCount++;
    Explain how to fix this problem in a simple way.

  13. Modify the cascaded if for computing grades, so that grade stores '?' if testScore is outside the range 0 to 100 inclusive.

  14. Write a trace table for the clock code, if the clock starts at 11:59pm (one minute before midnight).

  15. Assume that we declare double s, signum; Write a cascaded if statement(s) that stores into signum the value -1. if X is less than 0.; 0. if X is equal to 0.; 1. if X is greater than 0.

  16. Assume that we declare double min, x, max; Write a cascaded if statement(s) that stores into x the value min if x is less than min; max if x is greater than max; nothing new otherwise.

  17. Assume that we declare int x,y,z,min; Write an if statement(s) that stores into min the minimum of the values stored in x, y, and z. Try to do this with the minimum amount of code.

  18. Re-examine the cascade if that computes a course grade. Which of the following statements are equivalent to it (do the same thing for all values stored in testScore)?
        if (testScore >= 60)             if (testScore < 60)
          grade = 'D';                     grade = 'F';
        else if (testScore >= 70)        if (testScore < 70)
          grade = 'C';                     grade = 'D';
        else if (testScore >= 80)        if (testScore < 80)
          grade = 'B';                     grade = 'C';
        else if (testScore >= 90)        if (testScore < 90)
          grade = 'A';                     grade = 'B';
        else                             else
          grade = 'F';                     grade = 'A';
    What simple changes would correct any incorrect code?

  19. Suppose that we modify the clock code to call emitBeeps at the bottom of its block, and also change its argument to just hour. Will this code always work as before? If not, for what hour and minute combination(s) will it fail?
         if (minute != 59)
           minute++;
         else {
           minute = 0;
           if (hour != 23)
             hour++;
           else
             hour = 0;
           emitBeeps(hour);
         }
    Note that to be correct, the code must be correct for every hour and minute. There are 24x60 = 1,440 different possiblities; which ones are crucial to check?

  20. Suppose that we modify the clock code as follows. Will this code always work as before? If not, for what hour and minute will it fail?
         minute++;
         if (minute == 60} {
           minute = 0;
           hour++;
           emitBeeps(hour);
           if (hour == 24)
             hour = 0;
         }

    Note that to be correct, the code must be correct for every hour and minute. There are 24x60 = 1,440 different possiblities; which ones are crucial to check?

  21. Assume that we declare int hour; storing the values 0 through 23 as described above. Write an if statement(s) to display on the console the hour in a standard format: e.g., when hour stores 3 display 3am; when hour stores 15 display 3pm. When hour stores 0 display 12midnight and when hour stores 12 display 12noon. Try to do this with the simplest possible code.

  22. Write a standard trace table for the following code, when the user enter the value 3; write a compact trace table for this value, and when the user enters 7.
      int cycleCount = 0;
      int test = Prompt.forInt("Enter value to test");
      for (;;) {
        cycleCount++;
    
        //////////////
        if (test == 1)
          break;
        //////////////
    
        if (test%2 == 0)
          test = test/2;
        else
          test = 3*test + 1;
      }
      System.out.println("Finished in " + cycleCount + " cycles");

  23. The following two code fragments are identical to the original count down code, but the if appears in different locations inside the loop. Write a standard trace table for each showing its behavior and results. Can you change the test in the if to produce the original results?
       int countdown = 3;
       for (;;) {
         if (countdown == 0)
           break;
         System.out.println(countdown + "...");
         countdown--;
       }
       System.out.println("Blastoff");
    
    
       int countdown = 3;
       for (;;) {
         System.out.println(countdown + "...");
         countdown--;
         if (countdown == 0)
           break;
       }
       System.out.println("Blastoff");

  24. The following code fragment is identical to the original sentinel code, but the if appears in a different location inside the loop. Write a standard trace table for each showing its behavior and results.
        int count = 0;
        int sum   = 0;
        int score;
        for (;;) {
          score = Prompt.forInt("Enter a Score (-1 to Terminate)");
          count++;
          sum += score;
          if (score == -1)
            break;
        }
        System.out.println("Average = " + (double)Sum/(double)Score);

  25. Rewrite the countdown loop more compactly, so that it uses a general for statement (without if/break in its body).

  26. Rewrite the sentinel loop twice, so that it uses a while loop and a do loop. It can be done, but what problem arises?

  27. The following code can be used to ensure that the user enters a positive value. Write a standard trace table for the following code, assuming the user enters the values -5, -1, and 8 respectively.
      int positive;
      for (;;) {
        positive = Prompt.forInt("Enter Positive Value");
    
        //////////////
        if (positive >= 1)
          break;
        //////////////
    
        System.out.println("Sorry, " + positive + " isn't postive!");
      }
      System.out.println("positive = " + positive);
    What changes would be necessary to ensure the user entered a positive and even value?

  28. Write a compact trace table for the following code, assuming the user enters the values 4, 5, 7, -3, -7, 8, -8, 11, 15, -5, 9, and -100 respectively.
      int zcc = 0;
      int p = Prompt.forInt("Enter Value");
      for (;;) {
        int c;
        c = Prompt.forInt("Enter Value");
    
        //////////////
        if (c == -100)
          break;
        //////////////
    
        if (p < 0 != c < 0)
          zcc++;
    
        p = c;
      }
      System.out.println("Final zcc = " + zcc);

  29. Write a compact trace table for the following code, assuming the user enters the values 4, 5, 7, 3, 7, 7, 8, 11, 15, 5, 9, and -1 respectively.
      int ll = 0;
      int cl = 0;
      int p = Prompt.forInt("Enter Value");
      for (;;) {
        if (cl > ll)
          ll = cl;
       
        int c = Prompt.forInt"Enter Value")
    
        //////////////
        if (c == -1)
          break;
        //////////////
    
        if (p < c)
          cl++;
        else
          cl = 0
    
        p = c;
      }
      System.out.println("Final ll = " + ll);

  30. Write code (see the previous two problems for inspiration) that determines whether all the values it prompts for (use -1 as a sentinel) are in strictly increasing order: each value is greater than the one that precedes it. It should store its final answer in a boolean variable named isIncreasing. Write two compact trace tables for your code, assuming the user enters the values 4, 5, 3, 5, 2, 4, and -1 the first time; and 4, 5, 7, 7, -1 the second time.

  31. Write code that counts the number of strictly increasing pairs of values the user enters (use -1 as a sentinel). It should store its final answer in an int variable named increasingPairCount. Write two compact trace tables for your code, assuming the user enters the values 4, 5, 3, 3, 7, 6, 8, 9, 2, 5, 4, and -1. For this input, it finds 5 pairs: 4:5, 3:7, 6:8, 8:9, and 2:5.

  32. Explain why the following for loop contains an error spotted by the Java compiler
      for (int i=1; i<=10; i+1)
        System.out.println(i);

  33. Explain what the following for loop displays. Hint: it doesn't display all the even numbers from 1 to 10. How could we write a for loop to accomplish this task?
      for (int i=1; i<=10 && i%2==0; i++)
        System.out.println(i);

  34. Translate the following for loop into a for(;;) loop with a break in its body.
      for (int i=5; i>=0; i--)
        System.out.println(i);

  35. Assume that we have declared int Width, Height; and stored values into these variables. Write a pair of nested for loops that print a rectangle of '*' that is height by width: e.g., if height was 5 and width was 20 it would display
      ******************** 
      ******************** 
      ******************** 
      ******************** 
      ********************
  36. What is syntactically wrong with the following for loop (be careful)? How can we rewrite it to satisfy the syntax but still do the same intended thing?
        for (int i = 0, double d = 0.0; velocity>0 ; i++,d+=.01)

  37. Suppose that in the prompting code, we always want to set answer to 0 and terminate the loop if the user fails to enter a valid integer. Change the code to implement this requirement. Hint: this change requires just changing the block in the catch-clause.

  38. Explain whether or not the code below correctly reads all the integer values in a file and prints their sum. Note that this code contains a for loop in a try-catch statements with no break statement; the original code to solve this problem contained a try-catch (with a break statement) in a for loop.
      int sum = 0;
      try {
        for (;;) {
          int aValue = inputFile.readInt();
          sum += aValue;
        }
      }
      catch (EndOfFileException eofe) {}
      System.out.println("Sum = " + sum);

  39. Suppose that in the file-reading code (from the lecture), we want to ignore any non-integer values encountered (but keep reading more values until there are no more in the file to read). How can we change the code to implement this requirement? Why might it be better not just to ignore it, but print an error message (and how can this be accomplished)? Finally, why can't we modify the code above to solve this problem too? Hint: look at how catch-clause appears in the EBNF try-catch statements. Failing to read an int because some non-int value appears in the file will cause Java to throw the NumberFormatException.