Lecture 8

In this lecture we will discuss blocks and scopes, which define which parts of a program a declared variable can be used.  We will also discuss loops, which repeat blocks of statements in a controlled manner.

Variables Blocks, and Scopes

Variables come into existence when their declaration is reached.  They disappear (are destroyed) when the scope in which they are defined is exited.

Consider the following program:

public class Demo {
  private static int count;

  public static void f() {
    int a = 4;
    count = count + 1;

    System.out.println("a = " + a);

    if (count % 2 == 0) {
      int b = 5;
      System.out.println("count = " + count);
      System.out.println("inside block, a = " + a + ", b = " + b);
    }

    System.out.println("after if statement, a = " + a);

    // Can't refer to b here because it is no longer in scope
    System.out.println("after if statement, b = " + b);
  }

  public static void main(String[] args) {
    f();
    f();
    f();
    f();
  }
}

The scope of the static variable count is the entire execution of the program.  In other words, static variables come into existence when the program they are part of is executed, and are destroyed when the entire program completes.  Each static variable is a unique location in memory.  All references to a particular static variable---reading its value or storing a new value in it---refer to the single location.  That is why static variables "remember" the last value that was stored in them.

Variables that are defined inside a method are called local variables.  The scope of a local variable is determined by the inner most pair of curly braces ("{" and "}") that surrounds the declaration of the variable.  This pair of braces is a block, and it defines the scope of the variable.  A local variable comes into existence when its declaration is reached, and it is destroyed when the closing curly brace defining its scope is reached.  It is illegal to refer to a variable after its scope has been exited.  For example, in the program above the statement crossed out with a line is illegal because the block the local variable b is defined in has been exited.

Here is the output when this program is executed:

Welcome to DrJava.
> java Demo
a = 4
after if statement, a = 4
a = 4
count = 2
inside block, a = 4, b = 5
after if statement, a = 4
a = 4
after if statement, a = 4
a = 4
count = 4
inside block, a = 4, b = 5
after if statement, a = 4

The interesting part is the output showing the value of the count static variable (which is printed only when its value is even).  Because the variable is static, its value persists between invocations of the method f.

Loops

Thus far we have considered programs where one statement is executed after another in a linear sequence.  Loops allow a program to repeat a block of code some number of times in a controlled manner.  The two main forms of loop in Java are while loops and for loops.  Here are the general forms of both kinds of loops:

While loop For loop
A
while ( condition ) {
  body
}
B
A
for ( initialization ; condition ; increment ) {
  body
}
B

The following diagrams explain the precise meaning of both kinds of loops:

While loop For loop

In each case, the loop (while or for) continues executing the body of the loop as long as the condition is true.  An iteration is a single execution of the loop body.  When the condition becomes false, the loop terminates, and execution continues to the statement(s) immediately following the loop.  In these examples, "A" and "B" refer to arbitrary (possibly empty) sequences of statements that appear before and after the loop.  Thus, when the loop condition becomes false, execution jumps to the beginning of "B".

Let's look at some simple examples of while and for loops.  As a task, we'll use the example of counting from 1 to 10 and printing a message for each value.

While Loops

First, a while loop:

public class WhileLoopExample {
  public static void main(String[] args) {
    int count = 1;

    while (count <= 10) {
      System.out.println(count + "...");
      count = count + 1;
    }

    System.out.println("Blast off!");
  }
}

When we run the main method of this class in the DrJava interactions window we see the following output:

Welcome to DrJava.
> java WhileLoopExample
1...
2...
3...
4...
5...
6...
7...
8...
9...
10...
Blast off!

There are four important features in any loop:

  1. The loop variable: this is a variable that changes each time the body of the loop is executed

  2. The initial value of the loop variable

  3. The condition specifying when the loop should continue and when the loop should terminate

  4. The "increment"; this is a statement that updates the loop variable so its value will be different when the loop condition is re-tested following the execution of the loop body.  Note that the "increment" could actually be a decrement if the purpose of the loop is to count down from a higher initial value.

Here is the while loop annotated to highlight these features:

public class WhileLoopExample {
  public static void main(String[] args) {
    // Declare the loop variable and set its initial value to 1
    int count = 1;

    while (count <= 10) { // loop condition: loop continues while count <= 10
      System.out.println(count + "...");

      // increment: count increases by 1 at the end of each iteration
      count = count + 1;
    }

    System.out.println("Blast off!");
  }
}

For Loops

For loops are very similar to while loops.  They differ mainly in that they allow the declaration and initialization of the loop variable, the loop condition, and the increment to be specified together, resulting in more concise code.  Here is our countdown example written with a for loop instead of a while loop:

public class ForLoopExample {
  public static void main(String[] args) {
    for (int count = 1; count <= 10; count = count + 1) {
      System.out.println(count + "...");
    }
    System.out.println("Blast off!");
  }
}

Note that the loop variable declaration/initialization, loop condition, and increment are all written together (separated by semicolons).

One important detail to note about for loops concerns the scope of the loop variable.  Specifically, the scope of a loop variable declared by a for loop is the block defined by the body of the loop.  This means that the loop variable is no longer in scope once the loop has terminated.  So, the following program is illegal:

public class ForLoopExample {
  public static void main(String[] args) {
    for (int count = 1; count <= 10; count = count + 1) {
      System.out.println(count + "...");
    }
    System.out.println("Blast off!");
    System.out.println("final count is " + count); // Error!
  }
}

If we try to compile this program the Java compiler emits the following error message:

1 error found:
File: /home/daveho/ForLoopExample.java  [line: 7]
Error: cannot find symbol
symbol  : variable count
location: class ForLoopExample

Guidelines for Writing Loops

Here are two important guidelines for writing loops.

  1. The loop variable must change within the body of the loop.  If the loop variable does not change then the loop will most likely execute forever: this is an infinite loop.

  2. The condition must test the loop variable.  The condition needs to be something that, given the initial value of the loop variable and how it is modified within the loop body, will eventually become false, allowing the loop to terminate.  An incorrect loop condition can result in an infinite loop or a loop that executes the wrong number of iterations.

Accumulator Variables

Another type of variable useful in loops is an accumulator variable.  The accumulator (as the name suggests) accumulates the results of computations performed in the iterations of the loop body.  As a simple example, consider a static method that computes the sums of integers in a specified range.

public class SumInts {
  public static int computeSumOfInts(int min, int max) {
    int sum = 0;

    for (int i = min; i <= max; i = i + 1) {
      sum = sum + i;
    }

    return sum;
  }
}

We can test the functioning of this method in the DrJava interactions window:

Welcome to DrJava.
> SumInts.computeSumOfInts(1, 4)
10
> SumInts.computeSumOfInts(10, 12)
33

The method works by using an accumulator variable, sum, to store the sum of the integers defined by the range given by the min and max parameters.  The for loop uses a loop variable i to range over the integers between min and max, inclusive.  Each iteration of the loop adds i to sum: thus, when the loop terminates, sum will contain the sum of each integer in the range.

The idiom for using an accumulator variable is that the accumulator must be initialized to a value that is correct for the case where there are no iterations of the loop.  In the code above, the integer value zero is the correct value for the sum of an empty sequence of integers.  Each iteration of the loop must update the accumulator variable to reflect the value computed on that loop particular iteration.

Compound Assignments, Increment, and Decrement Statements

A common task, especially when programming with loops, is to update a variable to a new value that is based on the original value of the variable.  For example, we might be incrementing the value of an int loop variable called count by one:

count = count + 1;

This statement means

  1. compute the result of adding 1 to the value stored in the variable count
  2. store the computed value to the variable count

Because this kind of operation is so common, Java provides several convenient shortcuts.

Compound Assignment

Compound assignment allows any binary arithmetic operator to be combined with assignment as follows:

Compound Assignment Equivalent
a += n; a = a + n;
a -= n; a = a - n;
a *= n; a = a * n;
a /= n; a = a / n;
a %= n; a = a % n;

Increment/Decrement Statements

An even more concise way to increment or decrement a variable exists as long as you want to increase or decrease the variable's value by exactly one:

Increment/decrement Equivalent
i++; i = i + 1;
++i; i = i + 1;
i--; i = i - 1;
--i; i = i - 1;

Note that there are two forms of increment and decrement: pre and post.  The difference between these two forms has to do with the value of the increment or decrement if used in an expression.  Preincrement and predecrement yield the value of the variable before the update.  Postincrement and postdecrement yield the value of the variable after the update.  This can be seen in an example program:

public class IncrementDecrement {
  public static void main(String[] args) {
    int a = 5;
    int b = 5;
    int c = 5;
    int d = 5;
    
    System.out.println("a++ = " + a++);
    System.out.println("++b = " + ++b);
    System.out.println("c-- = " + c--);
    System.out.println("--d = " + --d);
    
    System.out.println("Finally, a = " + a +
                       ", b = " + b +
                       ", c = " + c +
                       ", d = " + d);
  }
}

Here is the output of the program:

Welcome to DrJava.
> java IncrementDecrement
a++ = 5
++b = 6
c-- = 5
--d = 4
Finally, a = 6, b = 6, c = 4, d = 4

As you can see, the "pre" versions of increment and decrement update the variable immediately, and the "post" versions wait until after the expression has been evaluated to perform the update.

Because the difference between pre and post increment is subtle and potentially confusing:

Do not use the value of a pre or post increment/decrement in an expression.

One place where you can reasonably use an increment/decrement expression is in a loop.  For example:

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