Javanotes5 51 101
Javanotes5 51 101
An enum is a type that has a fixed list of possible values, which is specified when the enum
is created. In some ways, an enum is similar to the boolean data type, which has true and
false as its only possible values. However, boolean is a primitive type, while an enum is not.
The definition of an enum types has the (simplified) form:
enum henum-type-name i { hlist-of-enum-values i }
This definition cannot be inside a subroutine. You can place it outside the main() routine
of the program. The henum-type-namei can be any simple identifier. This identifier becomes
the name of the enum type, in the same way that “boolean” is the name of the boolean type
and “String” is the name of the String type. Each value in the hlist-of-enum-valuesi must be a
simple identifier, and the identifiers in the list are separated by commas. For example, here is
the definition of an enum type named Season whose values are the names of the four seasons
of the year:
enum Season { SPRING, SUMMER, FALL, WINTER }
By convention, enum values are given names that are made up of upper case letters, but
that is a style guideline and not a syntax rule. Enum values are not variables. Each value is
a constant that always has the same value. In fact, the possible values of an enum type are
usually referred to as enum constants.
Note that the enum constants of type Season are considered to be “contained in” Season,
which means—following the convention that compound identifiers are used for things that are
contained in other things—the names that you actually use in your program to refer to them
are Season.SPRING, Season.SUMMER, Season.FALL, and Season.WINTER.
Once an enum type has been created, it can be used to declare variables in exactly the same
ways that other types are used. For example, you can declare a variable named vacation of
type Season with the statement:
Season vacation;
After declaring the variable, you can assign a value to it using an assignment statement. The
value on the right-hand side of the assignment can be one of the enum constants of type Season.
Remember to use the full name of the constant, including “Season”! For example:
vacation = Season.SUMMER;
You can print out an enum value with an output statement such as System.out.print(vacation).
The output value will be the name of the enum constant (without the “Season.”). In this case,
the output would be “SUMMER”.
Because an enum is technically a class, the enum values are technically objects. As ob-
jects, they can contain subroutines. One of the subroutines in every enum value is named
ordinal(). When used with an enum value, it returns the ordinal number of the value in
the list of values of the enum. The ordinal number simply tells the position of the value in
the list. That is, Season.SPRING.ordinal() is the int value 0, Season.SUMMER.ordinal() is
1, Season.FALL.ordinal() is 2, and Season.WINTER.ordinal() is 3. (You will see over and
over again that computer scientists like to start counting at zero!) You can, of course, use
the ordinal() method with a variable of type Season, such as vacation.ordinal() in our
example.
Right now, it might not seem to you that enums are all that useful. As you work though
the rest of the book, you should be convinced that they are. For now, you should at least
appreciate them as the first example of an important concept: creating new types. Here is a
little example that shows enums being used in a complete program:
36 CHAPTER 2. NAMES AND THINGS
Along these lines, I’ve written a class called TextIO that defines subroutines for reading
values typed by the user of a non-GUI program. The subroutines in this class make it possible
to get input from the standard input object, System.in, without knowing about the advanced
aspects of Java that are needed to use Scanner or to use System.in directly. TextIO also
contains a set of output subroutines. The output subroutines are similar to those provided in
System.out, but they provide a few additional features. You can use whichever set of output
subroutines you prefer, and you can even mix them in the same program.
To use the TextIO class, you must make sure that the class is available to your program.
What this means depends on the Java programming environment that you are using. In general,
you just have to add the source code file, TextIO.java, to the same directory that contains your
main program. See Section 2.6 for more information about how to use TextIO.
When the computer executes this statement, it will wait for the user to type in an integer
value. The value typed will be returned by the function, and it will be stored in the variable,
userInput. Here is a complete program that uses TextIO.getlnInt to read a number typed
by the user and then prints out the square of the number that the user types:
/**
* A program that reads an integer that is typed in by the
* user and computes and prints the square of that integer.
*/
} // end of main()
When you run this program, it will display the message “Please type a number:” and will
pause until you type a response, including a carriage return after the number.
The TextIO class contains static member subroutines TextIO.put and TextIO.putln that can
be used in the same way as System.out.print and System.out.println. For example, al-
though there is no particular advantage in doing so in this case, you could replace the two
lines
with
For the next few chapters, I will use TextIO for input in all my examples, and I will often use
it for output. Keep in mind that TextIO can only be used in a program if it is available to that
program. It is not built into Java in the way that the System class is.
Let’s look a little more closely at the built-in output subroutines System.out.print and
System.out.println. Each of these subroutines can be used with one parameter, where the
parameter can be a value of any of the primitive types byte, short, int, long, float, double, char,
or boolean. The parameter can also be a String, a value belonging to an enum type, or indeed
any object. That is, you can say “System.out.print(x);” or “System.out.println(x);”,
where x is any expression whose value is of any type whatsoever. The expression can be a con-
stant, a variable, or even something more complicated such as 2*distance*time. Now, in fact,
the System class actually includes several different subroutines to handle different parameter
types. There is one System.out.print for printing values of type double, one for values of
type int, another for values that are objects, and so on. These subroutines can have the same
name since the computer can tell which one you mean in a given subroutine call statement,
depending on the type of parameter that you supply. Having several subroutines of the same
name that differ in the types of their parameters is called overloading . Many programming
languages do not permit overloading, but it is common in Java programs.
The difference between System.out.print and System.out.println is that the println
version outputs a carriage return after it outputs the specified parameter value. There is a
version of System.out.println that has no parameters. This version simply outputs a carriage
return, and nothing else. A subroutine call statement for this version of the program looks like
“System.out.println();”, with empty parentheses. Note that “System.out.println(x);” is
exactly equivalent to “System.out.print(x); System.out.println();”; the carriage return
comes after the value of x. (There is no version of System.out.print without parameters.
Do you see why?)
As mentioned above, the TextIO subroutines TextIO.put and TextIO.putln can be used
as replacements for System.out.print and System.out.println. The TextIO functions work
in exactly the same way as the System functions, except that, as we will see below, TextIO can
also be used to write to other destinations.
2.4. TEXT INPUT AND OUTPUT 39
than one value from the same line of input. TextIO provides the following alternative input
functions to allow you to do this:
j = TextIO.getInt(); // Reads a value of type int.
y = TextIO.getDouble(); // Reads a value of type double.
a = TextIO.getBoolean(); // Reads a value of type boolean.
c = TextIO.getChar(); // Reads a value of type char.
w = TextIO.getWord(); // Reads one "word" as a value of type String.
The names of these functions start with “get” instead of “getln”. “Getln” is short for “get
line” and should remind you that the functions whose names begin with “getln” will get an
entire line of data. A function without the “ln” will read an input value in the same way, but
will then save the rest of the input line in a chunk of internal memory called the input buffer .
The next time the computer wants to read an input value, it will look in the input buffer before
prompting the user for input. This allows the computer to read several values from one line
of the user’s input. Strictly speaking, the computer actually reads only from the input buffer.
The first time the program tries to read input from the user, the computer will wait while the
user types in an entire line of input. TextIO stores that line in the input buffer until the data
on the line has been read or discarded (by one of the “getln” functions). The user only gets to
type when the buffer is empty.
Clearly, the semantics of input is much more complicated than the semantics of output!
Fortunately, for the majority of applications, it’s pretty straightforward in practice. You only
need to follow the details if you want to do something fancy. In particular, I strongly advise
you to use the “getln” versions of the input routines, rather than the “get” versions, unless you
really want to read several items from the same line of input, precisely because the semantics
of the “getln” versions is much simpler.
Note, by the way, that although the TextIO input functions will skip past blank spaces and
carriage returns while looking for input, they will not skip past other characters. For example,
if you try to read two ints and the user types “2,3”, the computer will read the first number
correctly, but when it tries to read the second number, it will see the comma. It will regard this
as an error and will force the user to retype the number. If you want to input several numbers
from one line, you should make sure that the user knows to separate them with spaces, not
commas. Alternatively, if you want to require a comma between the numbers, use getChar()
to read the comma before reading the second number.
There is another character input function, TextIO.getAnyChar(), which does not skip past
blanks or carriage returns. It simply reads and returns the next character typed by the user,
even if it’s a blank or carriage return. If the user typed a carriage return, then the char returned
by getAnyChar() is the special linefeed character ’\n’. There is also a function, TextIO.peek(),
that lets you look ahead at the next character in the input without actually reading it. After
you “peek” at the next character, it will still be there when you read the next item from input.
This allows you to look ahead and see what’s coming up in the input, so that you can take
different actions depending on what’s there.
The TextIO class provides a number of other functions. To learn more about them, you can
look at the comments in the source code file, TextIO.java.
(You might be wondering why there are only two output routines, print and println,
which can output data values of any type, while there is a separate input routine for each data
type. As noted above, in reality there are many print and println routines, one for each data
type. The computer can tell them apart based on the type of the parameter that you provide.
However, the input routines don’t have parameters, so the different input routines can only be
2.4. TEXT INPUT AND OUTPUT 41
are to be output. Here is a statement that will print a number in the proper format for a dollar
amount, where amount is a variable of type double:
System.out.printf( "%1.2f", amount );
TextIO can also do formatted output. The function TextIO.putf has the same
functionality as System.out.printf. Using TextIO, the above example would be:
TextIO.printf("%1.2",amount); and you could say TextIO.putln("%1.2f",principal);
instead of TextIO.putln(principal); in the Interest2 program to get the output in the
right format.
The output format of a value is specified by a format specifier . The format string (in
the simple cases that I cover here) contains one format specifier for each of the values that is
to be output. Some typical format specifiers are %d, %12d, %10s, %1.2f, %15.8e and %1.8g.
Every format specifier begins with a percent sign (%) and ends with a letter, possibly with some
extra formatting information in between. The letter specifies the type of output that is to be
produced. For example, in %d and %12d, the “d” specifies that an integer is to be written. The
“12” in %12d specifies the minimum number of spaces that should be used for the output. If
the integer that is being output takes up fewer than 12 spaces, extra blank spaces are added
in front of the integer to bring the total up to 12. We say that the output is “right-justified
in a field of length 12.” The value is not forced into 12 spaces; if the value has more than 12
digits, all the digits will be printed, with no extra spaces. The specifier %d means the same as
%1d; that is an integer will be printed using just as many spaces as necessary. (The “d,” by the
way, stands for “decimal” (base-10) numbers. You can use an “x” to output an integer value
in hexadecimal form.)
The letter “s” at the end of a format specifier can be used with any type of value. It
means that the value should be output in its default format, just as it would be in unformatted
output. A number, such as the “10” in %10s can be added to specify the (minimum) number
of characters. The “s” stands for “string,” meaning that the value is converted into a String
value in the usual way.
The format specifiers for values of type double are even more complicated. An “f”, as
in %1.2f, is used to output a number in “floating-point” form, that is with digits after the
decimal point. In %1.2f, the “2” specifies the number of digits to use after the decimal point.
The “1” specifies the (minimum) number of characters to output, which effectively means that
just as many characters as are necessary should be used. Similarly, %12.3f would specify a
floating-point format with 3 digits after the decimal point, right-justified in a field of length 12.
Very large and very small numbers should be written in exponential format, such as
6.00221415e23, representing “6.00221415 times 10 raised to the power 23.” A format speci-
fier such as %15.8e specifies an output in exponential form, with the “8” telling how many
digits to use after the decimal point. If you use “g” instead of “e”, the output will be in
floating-point form for small values and in exponential form for large values. In %1.8g, the
8 gives the total number of digits in the answer, including both the digits before the decimal
point and the digits after the decimal point.
In addition to format specifiers, the format string in a printf statement can include other
characters. These extra characters are just copied to the output. This can be a convenient way
to insert values into the middle of an output string. For example, if x and y are variables of
type int, you could say
System.out.printf("The product of %d and %d is %d", x, y, x*y);
When this statement is executed, the value of x is substituted for the first %d in the string, the
2.4. TEXT INPUT AND OUTPUT 43
value of y for the second %d, and the value of the expression x*y for the third, so the output
would be something like “The product of 17 and 42 is 714” (quotation marks not included in
output!).
After this statement is executed, any output from TextIO output statements will be sent to the
file named “result.txt” instead of to standard output. The file should be created in the same
directory that contains the program. Note that if a file with the same name already exists, its
previous contents will be erased! In many cases, you want to let the user select the file that
will be used for output. The statement
TextIO.writeUserSelectedFile();
will open a typical graphical-user-interface file selection dialog where the user can specify the
output file. If you want to go back to sending output to standard output, you can say
TextIO.writeStandardOutput();
You can also specify the input source for TextIO’s various “get” functions. The default input
source is standard input. You can use the statement TextIO.readFile("data.txt") to read
from a file named “data.txt” instead, or you can let the user select the input file by saying
TextIO.readUserSelectedFile(), and you can go back to reading from standard input with
TextIO.readStandardInput().
When your program is reading from standard input, the user gets a chance to correct any
errors in the input. This is not possible when the program is reading from a file. If illegal data
is found when a program tries to read from a file, an error occurs that will crash the program.
(Later, we will see that is is possible to “catch” such errors and recover from them.) Errors can
also occur, though more rarely, when writing to files.
A complete understanding of file input/output in Java requires a knowledge of object ori-
ented programming. We will return to the topic later, in Chapter 11. The file I/O capabilities
in TextIO are rather primitive by comparison. Nevertheless, they are sufficient for many appli-
cations, and they will allow you to get some experience with files sooner rather than later.
As a simple example, here is a program that asks the user some questions and outputs the
user’s responses to a file named “profile.txt”:
public class CreateProfile {
public static void main(String[] args) {
44 CHAPTER 2. NAMES AND THINGS
The Math class also contains a couple of mathematical constants that are useful in math-
ematical expressions: Math.PI represents π (the ratio of the circumference of a circle to its
diameter), and Math.E represents e (the base of the natural logarithms). These “constants”
are actually member variables in Math of type double. They are only approximations for the
mathematical constants, which would require an infinite number of digits to specify exactly.
Literals, variables, and function calls are simple expressions. More complex expressions
can be built up by using operators to combine simpler expressions. Operators include + for
adding two numbers, > for comparing two values, and so on. When several operators appear
in an expression, there is a question of precedence, which determines how the operators are
grouped for evaluation. For example, in the expression “A + B * C”, B*C is computed first
and then the result is added to A. We say that multiplication (*) has higher precedence
than addition (+). If the default precedence is not what you want, you can use parentheses to
explicitly specify the grouping you want. For example, you could use “(A + B) * C” if you
want to add A to B first and then multiply the result by C.
The rest of this section gives details of operators in Java. The number of operators in Java
is quite large, and I will not cover them all here. Most of the important ones are here; a few
will be covered in later chapters as they become relevant.
For example, -X has the same value as (-1)*X. For completeness, Java also has a unary plus
operator, as in +X, even though it doesn’t really do anything.
By the way, recall that the + operator can also be used to concatenate a value of any
type onto a String. This is another example of type conversion. In Java, any type can be
automatically converted into type String.
The effect of the assignment statement x = x + 1 is to take the old value of the variable
x, compute the result of adding 1 to that value, and store the answer as the new value of
x. The same operation can be accomplished by writing x++ (or, if you prefer, ++x). This
actually changes the value of x, so that it has the same effect as writing “x = x + 1”. The two
statements above could be written
counter++;
goalsScored++;
Similarly, you could write x-- (or --x) to subtract 1 from x. That is, x-- performs the same
computation as x = x - 1. Adding 1 to a variable is called incrementing that variable,
and subtracting 1 is called decrementing . The operators ++ and -- are called the increment
operator and the decrement operator, respectively. These operators can be used on variables
belonging to any of the numerical types and also on variables of type char.
Usually, the operators ++ or -- are used in statements like “x++;” or “x--;”. These state-
ments are commands to change the value of x. However, it is also legal to use x++, ++x, x--,
or --x as expressions, or as parts of larger expressions. That is, you can write things like:
y = x++;
y = ++x;
TextIO.putln(--x);
z = (++x) * (y--);
The statement “y = x++;” has the effects of adding 1 to the value of x and, in addition, assigning
some value to y. The value assigned to y is the value of the expression x++, which is defined
to be the old value of x, before the 1 is added. Thus, if the value of x is 6, the statement “y
= x++;” will change the value of x to 7, but it will change the value of y to 6 since the value
assigned to y is the old value of x. On the other hand, the value of ++x is defined to be the
new value of x, after the 1 is added. So if x is 6, then the statement “y = ++x;” changes the
values of both x and y to 7. The decrement operator, --, works in a similar way.
This can be confusing. My advice is: Don’t be confused. Use ++ and -- only in stand-alone
statements, not in expressions. I will follow this advice in all the examples in these notes.
to compare two values using a relational operator . Relational operators are used to test
whether two values are equal, whether one value is greater than another, and so forth. The
relational operators in Java are: ==, !=, <, >, <=, and >=. The meanings of these operators are:
A == B Is A "equal to" B?
A != B Is A "not equal to" B?
A < B Is A "less than" B?
A > B Is A "greater than" B?
A <= B Is A "less than or equal to" B?
A >= B Is A "greater than or equal to" B?
These operators can be used to compare values of any of the numeric types. They can also be
used to compare values of type char. For characters, < and > are defined according the numeric
Unicode values of the characters. (This might not always be what you want. It is not the same
as alphabetical order because all the upper case letters come before all the lower case letters.)
When using boolean expressions, you should remember that as far as the computer is con-
cerned, there is nothing special about boolean values. In the next chapter, you will see how to
use them in loop and branch statements. But you can also assign boolean-valued expressions
to boolean variables, just as you can assign numeric values to numeric variables.
By the way, the operators == and != can be used to compare boolean values. This is
occasionally useful. For example, can you figure out what this does:
boolean sameSign;
sameSign = ((x > 0) == (y > 0));
One thing that you cannot do with the relational operators <, >, <=, and <= is to use them
to compare values of type String. You can legally use == and != to compare Strings, but
because of peculiarities in the way objects behave, they might not give the results you want.
(The == operator checks whether two objects are stored in the same memory location, rather
than whether they contain the same value. Occasionally, for some objects, you do want to make
such a check—but rarely for strings. I’ll get back to this in a later chapter.) Instead, you should
use the subroutines equals(), equalsIgnoreCase(), and compareTo(), which were described
in Section 2.3, to compare two Strings.
Suppose that the value of x is in fact zero. In that case, the division y/x is undefined math-
matically. However, the computer will never perform the division, since when the computer
evaluates (x != 0), it finds that the result is false, and so it knows that ((x != 0) && any-
thing) has to be false. Therefore, it doesn’t bother to evaluate the second operand, (y/x > 1).
The evaluation has been short-circuited and the division by zero is avoided. Without the short-
circuiting, there would have been a division by zero. (This may seem like a technicality, and it
is. But at times, it will make your programming life a little easier.)
The boolean operator “not” is a unary operator. In Java, it is indicated by ! and is written
in front of its single operand. For example, if test is a boolean variable, then
test = ! test;
will reverse the value of test, changing it from true to false, or from false to true.
A = 17;
X = A; // OK; A is converted to a double
B = A; // illegal; no automatic conversion
// from int to short
The idea is that conversion should only be done automatically when it can be done without
changing the semantics of the value. Any int can be converted to a double with the same
numeric value. However, there are int values that lie outside the legal range of shorts. There
is simply no way to represent the int 100000 as a short, for example, since the largest value of
type short is 32767.
In some cases, you might want to force a conversion that wouldn’t be done automatically.
For this, you can use what is called a type cast. A type cast is indicated by putting a type
name, in parentheses, in front of the value you want to convert. For example,
int A;
short B;
A = 17;
B = (short)A; // OK; A is explicitly type cast
// to a value of type short
You can do type casts from any numeric type to any other numeric type. However, you should
note that you might change the numeric value of a number by type-casting it. For example,
(short)100000 is -31072. (The -31072 is obtained by taking the 4-byte int 100000 and throwing
away two of those bytes to obtain a short—you’ve lost the real information that was in those
two bytes.)
As another example of type casts, consider the problem of getting a random integer between
1 and 6. The function Math.random() gives a real number between 0.0 and 0.9999. . . , and so
6*Math.random() is between 0.0 and 5.999. . . . The type-cast operator, (int), can be used to
convert this to an integer: (int)(6*Math.random()). A real number is cast to an integer by
discarding the fractional part. Thus, (int)(6*Math.random()) is one of the integers 0, 1, 2, 3,
4, and 5. To get a number between 1 and 6, we can add 1: “(int)(6*Math.random()) + 1”.
You can also type-cast between the type char and the numeric types. The numeric value
of a char is its Unicode code number. For example, (char)97 is ’a’, and (int)’+’ is 43.
(However, a type conversion from char to int is automatic and does not have to be indicated
with an explicit type cast.)
Java has several variations on the assignment operator, which exist to save typing. For
example, “A += B” is defined to be the same as “A = A + B”. Every operator in Java that
applies to two operands gives rise to a similar assignment operator. For example:
x -= y; // same as: x = x - y;
x *= y; // same as: x = x * y;
x /= y; // same as: x = x / y;
x %= y; // same as: x = x % y; (for integers x and y)
q &&= p; // same as: q = q && p; (for booleans q and p)
The combined assignment operator += even works with strings. Recall that when the + operator
is used with a string as one of the operands, it represents concatenation. Since str += x is
equivalent to str = str + x, when += is used with a string on the left-hand side, it appends
the value on the right-hand side onto the string. For example, if str has the value “tire”, then
the statement str += ’d’; changes the value of str to “tired”.
50 CHAPTER 2. NAMES AND THINGS
then the name of the type conversion function would be Suit.valueOf. The value of the
function call Suit.valueOf("CLUB") would be the enumerated type value Suit.CLUB. For the
conversion to succeed, the string must exactly match the simple name of one of the enumerated
type constants (without the “Suit.” in front).
Operators on the same line have the same precedence. When operators of the same precedence
are strung together in the absence of parentheses, unary operators and assignment operators are
evaluated right-to-left, while the remaining operators are evaluated left-to-right. For example,
A*B/C means (A*B)/C, while A=B=C means A=(B=C). (Can you see how the expression A=B=C
might be useful, given that the value of B=C as an expression is the same as the value that is
assigned to B?)
To test the java command, copy sample program Interest2.java from this book’s source
directory into your working directory. First, compile the program with the command
javac Interest2.java
Remember that for this to succeed, TextIO must already be in the same directory. Then you
can execute the program using the command
java Interest2
Be careful to use just the name of the program, Interest2, not the name of the Java source
code file or the name of the compiled class file. When you give this command, the program will
run. You will be asked to enter some information, and you will respond by typing your answers
into the command window, pressing return at the end of the line. When the program ends, you
will see the command prompt, and you can enter another command.
You can follow the same procedure to run all of the examples in the early sections of this
book. When you start work with applets, you will need a different command to execute the
applets. That command will be introduced later in the book.
∗ ∗ ∗
To create your own programs, you will need a text editor . A text editor is a computer
program that allows you to create and save documents that contain plain text. It is important
that the documents be saved as plain text, that is without any special encoding or formatting
information. Word processor documents are not appropriate, unless you can get your word
processor to save as plain text. A good text editor can make programming a lot more pleasant.
Linux comes with several text editors. On Windows, you can use notepad in a pinch, but you
will probably want something better. For Mac OS, you might download the free TextWrangler
application. One possibility that will work on any platform is to use jedit, a good programmer’s
text editor that is itself written in Java and that can be downloaded for free from www.jedit.org.
To create your own programs, you should open a command line window and cd into the
working directory where you will store your source code files. Start up your text editor program,
such as by double-clicking its icon or selecting it from a Start menu. Type your code into the
editor window, or open an existing source code file that you want to modify. Save the file.
Remember that the name of a Java source code file must end in “.java”, and the rest of the
file name must match the name of the class that is defined in the file. Once the file is saved in
your working directory, go to the command window and use the javac command to compile it,
as discussed above. If there are syntax errors in the code, they will be listed in the command
window. Each error message contains the line number in the file where the computer found the
error. Go back to the editor and try to fix the errors, save your changes, and they try the
javac command again. (It’s usually a good idea to just work on the first few errors; sometimes
fixing those will make other errors go away.) Remember that when the javac command finally
succeeds, you will get no message at all. Then you can use the java command to run your
program, as described above. Once you’ve compiled the program, you can run it as many times
as you like without recompiling it.
That’s really all there is to it: Keep both editor and command-line window open. Edit,
save, and compile until you have eliminated all the syntax errors. (Always remember to save
the file before compiling it—the compiler only sees the saved file, not the version in the editor
window.) When you run the program, you might find that it has semantic errors that cause it
to run incorrectly. It that case, you have to go back to the edit/save/compile loop to try to
find and fix the problem.
54 CHAPTER 2. NAMES AND THINGS
up, make sure “Java Project” is selected, and click the “Next” button. In the next window,
it should only be necessary to fill in a “Project Name” for the project and click the “Finish”
button. The project should appear in the “Package Explorer” view. Click on the small triangle
next to the project name to see the contents of the project. At the beginning, it contains only
the “JRE System Library”; this is the collection of standard built-in classes that come with
Java.
To run the TextIO based examples from this textbook, you must add the source code file
TextIO.java to your project. If you have downloaded the Web site of this book, you can find a
copy of TextIO.java in the source directory. Alternatively, you can navigate to the file on-line
and use the “Save As” command of your Web browser to save a copy of the file onto your
computer. The easiest way to get TextIO into your project is to locate the source code file on
your computer and drag the file icon onto the project name in the Eclipse window. If that
doesn’t work, you can try using copy-and-paste: Right-click the file icon (or control-click on
Mac OS), select “Copy” from the pop-up menu, right-click the project name in the Eclipse
window, and select “Paste”. If you also have trouble with that, you can try using the “Import”
command in the “File” menu; select “File system” in the window that pops up, click “Next”,
and provide the necessary information in the next window. (Unfortunately, using the file import
window is rather complicated. If you find that you have to use it, you should consult the Eclipse
documentation about it.) In any case, TextIO should appear in your project, inside a package
named “default package”. You will need to click the small triangle next to “default package”
to see the file. Once a file is in this list, you can open it by double-clicking it; it will appear in
the editing area of the Eclipse window.
To run any of the Java programs from this textbook, copy the source code file into your
Eclipse Java project. To run the program, right-click the file name in the Package Explorer
view (or control-click in Mac OS). In the menu that pops up, go to the “Run As” submenu, and
select “Java Application”. The program will be executed. If the program writes to standard
output, the output will appear in the “Console” view, under the editing area. If the program
uses TextIO for input, you will have to type the required input into the “Console” view—click
the “Console” view before you start typing, so that the characters that you type will be sent to
the correct part of the window. (Note that if you don’t like doing I/O in the “Console” view,
you can use an alternative version of TextIO.java that opens a separate window for I/O. You
can find this “GUI” version of TextIO in a directory named TextIO-GUI inside this textbook’s
source directory.)
You can have more than one program in the same Eclipse project, or you can create addi-
tional projects to organize your work better. Remember to place a copy of TextIO.java in any
project that requires it.
∗ ∗ ∗
To create your own Java program, you must create a new Java class. To do this, right-click
the Java project name in the “Project Explorer” view. Go to the “New” submenu of the popup
menu, and select “Class”. In the window that opens, type in the name of the class, and click
the “Finish” button. Note that you want the name of the class, not the name of the source code
file, so don’t add “.java” at the end of the name. The class should appear inside the “default
package,” and it should automatically open in the editing area so that you can start typing in
your program.
Eclipse has several features that aid you as you type your code. It will underline any syntax
error with a jagged red line, and in some cases will place an error marker in the left border
of the edit window. If you hover the mouse cursor over the error marker, a description of the
56 CHAPTER 2. NAMES AND THINGS
error will appear. Note that you do not have to get rid of every error immediately as you type;
some errors will go away as you type in more of the program. If an error marker displays a
small “light bulb,” Eclipse is offering to try to fix the error for you. Click the light bulb to get
a list of possible fixes, then double click the fix that you want to apply. For example, if you
use an undeclared variable in your program, Eclipse will offer to declare it for you. You can
actually use this error-correcting feature to get Eclipse to write certain types of code for you!
Unfortunately, you’ll find that you won’t understand a lot of the proposed fixes until you learn
more about the Java language.
Another nice Eclipse feature is code assist. Code assist can be invoked by typing Control-
Space. It will offer possible completions of whatever you are typing at the moment. For example,
if you type part of an identifier and hit Control-Space, you will get a list of identifiers that start
with the characters that you have typed; use the up and down arrow keys to select one of the
items in the list, and press Return or Enter. (Or hit Escape to dismiss the list.) If there is
only one possible completion when you hit Control-Space, it will be inserted automatically. By
default, Code Assist will also pop up automatically, after a short delay, when you type a period
or certain other characters. For example, if you type “TextIO.” and pause for just a fraction
of a second, you will get a list of all the subroutines in the TextIO class. Personally, I find this
auto-activation annoying. You can disable it in the Eclipse Preferences. (Look under Java /
Editor / Code Assist, and turn off the “Enable auto activation” option.) You can still call up
Code Assist manually with Control-Space.
Once you have an error-free program, you can run it as described above, by right-clicking its
name in the Package Explorer and using “Run As / Java Application”. If you find a problem
when you run it, it’s very easy to go back to the editor, make changes, and run it again. Note
that using Eclipse, there is no explicit “compile” command. The source code files in your
project are automatically compiled, and are re-compiled whenever you modify them.
Although I have only talked about Eclipse here, if you are using a different IDE, you will
probably find a lot of similarities. Most IDEs use the concept of a “project” to which you have
to add your source code files, and most of them have menu commands for running a program.
All of them, of course, come with built-in text editors.
In an IDE, this will not cause any problem unless the program you are writing depends on
TextIO. You will not be able to use TextIO in a program unless TextIO is placed into the same
package as the program. This means that you have to modify the source code file TextIO.java
2.6. PROGRAMMING ENVIRONMENTS 57
to specify the package; just add a package statement using the same package name as the
program. Then add the modified TextIO.java to the same folder that contains the program
source code. Once you’ve done this, the example should run in the same way as if it were in
the default package.
By the way, if you use packages in a command-line environment, other complications arise.
For example, if a class is in a package named testpkg, then the source code file must be in
a subdirectory named testpkg that is inside your main Java working directory. Nevertheless,
when you compile or execute the program, you should be in the main directory, not in the
subdirectory. When you compile the source code file, you have to include the name of the
directory in the command: Use “javac testpkg/ClassName.java” on Linux or Mac OS, or
“javac testpkg\ClassName.java” on Windows. The command for executing the program is
then “java testpkg.ClassName”, with a period separating the package name from the class
name. Since packages can contain subpackages, it can get even worse than this! However, you
will not need to worry about any of that when using the examples in this book.
58 CHAPTER 2. NAMES AND THINGS
1. Write a program that will print your initials to standard output in letters that are nine
lines tall. Each big letter should be made up of a bunch of *’s. For example, if your initials
were “DJE”, then the output would look something like:
****** ************* **********
** ** ** **
** ** ** **
** ** ** **
** ** ** ********
** ** ** ** **
** ** ** ** **
** ** ** ** **
***** **** **********
2. Write a program that simulates rolling a pair of dice. You can simulate rolling one die by
choosing one of the integers 1, 2, 3, 4, 5, or 6 at random. The number you pick represents
the number on the die after it is rolled. As pointed out in Section 2.5, The expression
(int)(Math.random()*6) + 1
does the computation you need to select a random integer between 1 and 6. You can
assign this value to a variable to represent one of the dice that are being rolled. Do this
twice and add the results together to get the total roll. Your program should report the
number showing on each die as well as the total roll. For example:
The first die comes up 3
The second die comes up 5
Your total roll is 8
3. Write a program that asks the user’s name, and then greets the user by name. Before
outputting the user’s name, convert it to upper case letters. For example, if the user’s
name is Fred, then the program should respond “Hello, FRED, nice to meet you!”.
4. Write a program that helps the user count his change. The program should ask how many
quarters the user has, then how many dimes, then how many nickels, then how many
pennies. Then the program should tell the user how much money he has, expressed in
dollars.
5. If you have N eggs, then you have N/12 dozen eggs, with N%12 eggs left over. (This is
essentially the definition of the / and % operators for integers.) Write a program that asks
the user how many eggs she has and then tells the user how many dozen eggs she has and
how many extra eggs are left over.
A gross of eggs is equal to 144 eggs. Extend your program so that it will tell the user
how many gross, how many dozen, and how many left over eggs she has. For example, if
the user says that she has 1342 eggs, then your program would respond with
Your number of eggs is 9 gross, 3 dozen, and 10
Exercises 59
6. Suppose that a file named “testdata.txt” contains the following information: The first
line of the file is the name of a student. Each of the next three lines contains an integer.
The integers are the student’s scores on three exams. Write a program that will read
the information in the file and display (on standard output) a message the contains the
name of the student and the student’s average grade on the three exams. The average is
obtained by adding up the individual exam grades and then dividing by the number of
exams.
60 CHAPTER 2. NAMES AND THINGS
Quiz on Chapter 2
1. Briefly explain what is meant by the syntax and the semantics of a programming language.
Give an example to illustrate the difference between a syntax error and a semantics error.
2. What does the computer do when it executes a variable declaration statement. Give an
example.
4. One of the primitive types in Java is boolean. What is the boolean type? Where are
boolean values used? What are its possible values?
6. Explain what is meant by an assignment statement, and give an example. What are
assignment statements used for?
8. What is a literal?
9. In Java, classes have two fundamentally different purposes. What are they?
10. What is the difference between the statement “x = TextIO.getDouble();” and the state-
ment “x = TextIO.getlnDouble();”
11. Explain why the value of the expression 2 + 3 + "test" is the string "5test" while the
value of the expression "test" + 2 + 3 is the string "test23". What is the value of
"test" + 2 * 3 ?
12. Integrated Development Environments such as Eclipse often use syntax coloring , which
assigns various colors to the characters in a program to reflect the syntax of the language.
A student notices that Eclipse colors the word String differently from int, double, and
boolean. The student asks why String should be a different color, since all these words
are names of types. What’s the answer to the student’s question?
Chapter 3
3.1.1 Blocks
The block is the simplest type of structured statement. Its purpose is simply to group a
sequence of statements into a single statement. The format of a block is:
{
hstatements i
}
61
62 CHAPTER 3. CONTROL
That is, it consists of a sequence of statements enclosed between a pair of braces, “{” and “}”.
(In fact, it is possible for a block to contain no statements at all; such a block is called an
empty block , and can actually be useful at times. An empty block consists of nothing but
an empty pair of braces.) Block statements usually occur inside other statements, where their
purpose is to group together several statements into a unit. However, a block can be legally
used wherever a statement can occur. There is one place where a block is required: As you
might have already noticed in the case of the main subroutine of a program, the definition of a
subroutine is a block, since it is a sequence of statements enclosed inside a pair of braces.
I should probably note again at this point that Java is what is called a free-format language.
There are no syntax rules about how the language has to be arranged on a page. So, for example,
you could write an entire block on one line if you want. But as a matter of good programming
style, you should lay out your program on the page in a way that will make its structure as
clear as possible. In general, this means putting one statement per line and using indentation
to indicate statements that are contained inside control structures. This is the format that I
will generally use in my examples.
Here are two examples of blocks:
{
System.out.print("The answer is ");
System.out.println(ans);
}
In the second example, a variable, temp, is declared inside the block. This is perfectly legal,
and it is good style to declare a variable inside a block if that variable is used nowhere else
but inside the block. A variable declared inside a block is completely inaccessible and invisible
from outside that block. When the computer executes the variable declaration statement, it
allocates memory to hold the value of the variable. When the block ends, that memory is
discarded (that is, made available for reuse). The variable is said to be local to the block.
There is a general concept called the “scope” of an identifier. The scope of an identifier is the
part of the program in which that identifier is valid. The scope of a variable defined inside a
block is limited to that block, and more specifically to the part of the block that comes after
the declaration of the variable.
generally a bad thing. (There is an old story about computer pioneer Grace Murray Hopper,
who read instructions on a bottle of shampoo telling her to “lather, rinse, repeat.” As the story
goes, she claims that she tried to follow the directions, but she ran out of shampoo. (In case
you don’t get it, this is a joke about the way that computers mindlessly follow instructions.))
To be more specific, a while loop will repeat a statement over and over, but only so long
as a specified condition remains true. A while loop has the form:
while (hboolean-expression i)
hstatement i
Since the statement can be, and usually is, a block, many while loops have the form:
while (hboolean-expression i) {
hstatements i
}
The semantics of this statement go like this: When the computer comes to a while state-
ment, it evaluates the hboolean-expressioni, which yields either true or false as the value. If
the value is false, the computer skips over the rest of the while loop and proceeds to the next
command in the program. If the value of the expression is true, the computer executes the
hstatementi or block of hstatementsi inside the loop. Then it returns to the beginning of the
while loop and repeats the process. That is, it re-evaluates the hboolean-expressioni, ends the
loop if the value is false, and continues it if the value is true. This will continue over and over
until the value of the expression is false; if that never happens, then there will be an infinite
loop.
Here is an example of a while loop that simply prints out the numbers 1, 2, 3, 4, 5:
int number; // The number to be printed.
number = 1; // Start with 1.
while ( number < 6 ) { // Keep going as long as number is < 6.
System.out.println(number);
number = number + 1; // Go on to the next number.
}
System.out.println("Done!");
The variable number is initialized with the value 1. So the first time through the while loop,
when the computer evaluates the expression “number < 6”, it is asking whether 1 is less than 6,
which is true. The computer therefor proceeds to execute the two statements inside the loop.
The first statement prints out “1”. The second statement adds 1 to number and stores the
result back into the variable number; the value of number has been changed to 2. The computer
has reached the end of the loop, so it returns to the beginning and asks again whether number is
less than 6. Once again this is true, so the computer executes the loop again, this time printing
out 2 as the value of number and then changing the value of number to 3. It continues in this
way until eventually number becomes equal to 6. At that point, the expression “number < 6”
evaluates to false. So, the computer jumps past the end of the loop to the next statement
and prints out the message “Done!”. Note that when the loop ends, the value of number is 6,
but the last value that was printed was 5.
By the way, you should remember that you’ll never see a while loop standing by itself
in a real program. It will always be inside a subroutine which is itself defined inside some
class. As an example of a while loop used inside a complete program, here is a little program
that computes the interest on an investment over several years. This is an improvement over
examples from the previous chapter that just reported the results for one year:
64 CHAPTER 3. CONTROL
/*
This class implements a simple program that
will compute the amount of interest that is
earned on an investment over a period of
5 years. The initial amount of the investment
and the interest rate are input by the user.
The value of the investment at the end of each
year is output.
*/
/* Get the initial investment and interest rate from the user. */
years = 0;
while (years < 5) {
double interest; // Interest for this year.
interest = principal * rate;
principal = principal + interest; // Add it to principal.
years = years + 1; // Count the current year.
System.out.print("The value of the investment after ");
System.out.print(years);
System.out.print(" years is $");
System.out.printf("%1.2f", principal);
System.out.println();
} // end of while loop
} // end of main()
You should study this program, and make sure that you understand what the computer does
step-by-step as it executes the while loop.
if ( hboolean-expression i )
hstatement i
else
hstatement i
When the computer executes an if statement, it evaluates the boolean expression. If the value
is true, the computer executes the first statement and skips the statement that follows the
“else”. If the value of the expression is false, then the computer skips the first statement and
executes the second one. Note that in any case, one and only one of the two statements inside
the if statement is executed. The two statements represent alternative courses of action; the
computer decides between these courses of action based on the value of the boolean expression.
In many cases, you want the computer to choose between doing something and not doing
it. You can do this with an if statement that omits the else part:
if ( hboolean-expression i )
hstatement i
To execute this statement, the computer evaluates the expression. If the value is true, the
computer executes the hstatementi that is contained inside the if statement; if the value is
false, the computer skips that hstatementi.
Of course, either or both of the hstatementi’s in an if statement can be a block, so that an
if statement often looks like:
if ( hboolean-expression i ) {
hstatements i
}
else {
hstatements i
}
or:
if ( hboolean-expression i ) {
hstatements i
}
As an example, here is an if statement that exchanges the value of two variables, x and y,
but only if x is greater than y to begin with. After this if statement has been executed, we
can be sure that the value of x is definitely less than or equal to the value of y:
if ( x > y ) {
int temp; // A temporary variable for use in this block.
temp = x; // Save a copy of the value of x in temp.
x = y; // Copy the value of y into x.
y = temp; // Copy the value of temp into y.
}
Finally, here is an example of an if statement that includes an else part. See if you can
figure out what it does, and why it would be used:
if ( years > 1 ) { // handle case for 2 or more years
System.out.print("The value of the investment after ");
System.out.print(years);
System.out.print(" years is $");
}
else { // handle case for 1 year
66 CHAPTER 3. CONTROL
I’ll have more to say about control structures later in this chapter. But you already know
the essentials. If you never learned anything more about control structures, you would already
know enough to perform any possible computing task. Simple looping and branching are all
you really need!
top-down design. As you proceed through the stages of stepwise refinement, you can write out
descriptions of your algorithm in pseudocode—informal instructions that imitate the structure
of programming languages without the complete detail and perfect syntax of actual program
code.
As an example, let’s see how one might develop the program from the previous section, which
computes the value of an investment over five years. The task that you want the program to
perform is: “Compute and display the value of an investment for each of the next five years,
where the initial investment and interest rate are to be specified by the user.” You might then
write—or at least think—that this can be expanded as:
Get the user’s input
Compute the value of the investment after 1 year
Display the value
Compute the value after 2 years
Display the value
Compute the value after 3 years
Display the value
Compute the value after 4 years
Display the value
Compute the value after 5 years
Display the value
This is correct, but rather repetitive. And seeing that repetition, you might notice an
opportunity to use a loop. A loop would take less typing. More important, it would be more
general: Essentially the same loop will work no matter how many years you want to process.
So, you might rewrite the above sequence of steps as:
Get the user’s input
while there are more years to process:
Compute the value after the next year
Display the value
Following this algorithm would certainly solve the problem, but for a computer, we’ll have
to be more explicit about how to “Get the user’s input,” how to “Compute the value after the
next year,” and what it means to say “there are more years to process.” We can expand the
step, “Get the user’s input” into
Ask the user for the initial investment
Read the user’s response
Ask the user for the interest rate
Read the user’s response
To fill in the details of the step “Compute the value after the next year,” you have to
know how to do the computation yourself. (Maybe you need to ask your boss or professor for
clarification?) Let’s say you know that the value is computed by adding some interest to the
previous value. Then we can refine the while loop to:
while there are more years to process:
Compute the interest
Add the interest to the value
Display the value
As for testing whether there are more years to process, the only way that we can do that is
by counting the years ourselves. This displays a very common pattern, and you should expect
to use something similar in a lot of programs: We have to start with zero years, add one each
68 CHAPTER 3. CONTROL
time we process a year, and stop when we reach the desired number of years. So the while
loop becomes:
years = 0
while years < 5:
years = years + 1
Compute the interest
Add the interest to the value
Display the value
We still have to know how to compute the interest. Let’s say that the interest is to be
computed by multiplying the interest rate by the current value of the investment. Putting
this together with the part of the algorithm that gets the user’s inputs, we have the complete
algorithm:
Ask the user for the initial investment
Read the user’s response
Ask the user for the interest rate
Read the user’s response
years = 0
while years < 5:
years = years + 1
Compute interest = value * interest rate
Add the interest to the value
Display the value
Finally, we are at the point where we can translate pretty directly into proper programming-
language syntax. We still have to choose names for the variables, decide exactly what we want
to say to the user, and so forth. Having done this, we could express our algorithm in Java as:
double principal, rate, interest; // declare the variables
int years;
System.out.print("Type initial investment: ");
principal = TextIO.getlnDouble();
System.out.print("Type interest rate: ");
rate = TextIO.getlnDouble();
years = 0;
while (years < 5) {
years = years + 1;
interest = principal * rate;
principal = principal + interest;
System.out.println(principal);
}
This still needs to be wrapped inside a complete program, it still needs to be commented,
and it really needs to print out more information in a nicer format for the user. But it’s
essentially the same program as the one in the previous section. (Note that the pseudocode
algorithm uses indentation to show which statements are inside the loop. In Java, indentation
is completely ignored by the computer, so you need a pair of braces to tell the computer which
statements are in the loop. If you leave out the braces, the only statement inside the loop would
be “years = years + 1;". The other statements would only be executed once, after the loop
ends. The nasty thing is that the computer won’t notice this error for you, like it would if you
left out the parentheses around “(years < 5)”. The parentheses are required by the syntax of
3.2. ALGORITHM DEVELOPMENT 69
the while statement. The braces are only required semantically. The computer can recognize
syntax errors but not semantic errors.)
One thing you should have noticed here is that my original specification of the problem—
“Compute and display the value of an investment for each of the next five years”—was far from
being complete. Before you start writing a program, you should make sure you have a complete
specification of exactly what the program is supposed to do. In particular, you need to know
what information the program is going to input and output and what computation it is going
to perform. Here is what a reasonably complete specification of the problem might look like in
this example:
“Write a program that will compute and display the value of
an investment for each of the next five years. Each year, interest
is added to the value. The interest is computed by multiplying
the current value by a fixed interest rate. Assume that the initial
value and the rate of interest are to be input by the user when the
program is run.”
In order to compute the next term, the computer must take different actions depending on
whether N is even or odd. We need an if statement to decide between the two cases:
Get a positive integer N from the user;
while N is not 1:
if N is even:
Compute N = N/2;
else
Compute N = 3 * N + 1;
Output N;
Count this term;
Output the number of terms;
We are almost there. The one problem that remains is counting. Counting means that you
start with zero, and every time you have something to count, you add one. We need a variable
to do the counting. (Again, this is a common pattern that you should expect to see over and
over.) With the counter added, we get:
Get a positive integer N from the user;
Let counter = 0;
while N is not 1:
if N is even:
Compute N = N/2;
else
Compute N = 3 * N + 1;
Output N;
Add 1 to counter;
Output the counter;
We still have to worry about the very first step. How can we get a positive integer from the
user? If we just read in a number, it’s possible that the user might type in a negative number
or zero. If you follow what happens when the value of N is negative or zero, you’ll see that the
program will go on forever, since the value of N will never become equal to 1. This is bad. In
this case, the problem is probably no big deal, but in general you should try to write programs
that are foolproof. One way to fix this is to keep reading in numbers until the user types in a
positive number:
Ask user to input a positive number;
Let N be the user’s response;
while N is not positive:
Print an error message;
Read another value for N;
Let counter = 0;
while N is not 1:
if N is even:
Compute N = N/2;
else
Compute N = 3 * N + 1;
Output N;
Add 1 to counter;
Output the counter;
The first while loop will end only when N is a positive number, as required. (A common
beginning programmer’s error is to use an if statement instead of a while statement here:
“If N is not positive, ask the user to input another value.” The problem arises if the second
3.2. ALGORITHM DEVELOPMENT 71
number input by the user is also non-positive. The if statement is only executed once, so the
second input number is never tested. With the while loop, after the second number is input,
the computer jumps back to the beginning of the loop and tests whether the second number
is positive. If not, it asks the user for a third number, and it will continue asking for numbers
until the user enters an acceptable input.)
Here is a Java program implementing this algorithm. It uses the operators <= to mean “is
less than or equal to” and != to mean “is not equal to.” To test whether N is even, it uses
“N % 2 == 0”. All the operators used here were discussed in Section 2.5.
/**
* This program prints out a 3N+1 sequence starting from a positive
* integer specified by the user. It also counts the number of
* terms in the sequence, and prints out that number.
*/
public class ThreeN1 {
counter = 0;
while (N != 1) {
if (N % 2 == 0)
N = N / 2;
else
N = 3 * N + 1;
TextIO.putln(N);
counter = counter + 1;
}
TextIO.putln();
TextIO.put("There were ");
TextIO.put(counter);
TextIO.putln(" terms in the sequence.");
} // end of main()
Two final notes on this program: First, you might have noticed that the first term of the
sequence—the value of N input by the user—is not printed or counted by this program. Is
this an error? It’s hard to say. Was the specification of the program careful enough to decide?
This is the type of thing that might send you back to the boss/professor for clarification. The
problem (if it is one!) can be fixed easily enough. Just replace the line “counter = 0” before
the while loop with the two lines:
72 CHAPTER 3. CONTROL
Second, there is the question of why this problem is at all interesting. Well, it’s interesting
to mathematicians and computer scientists because of a simple question about the problem that
they haven’t been able to answer: Will the process of computing the 3N+1 sequence finish after
a finite number of steps for all possible starting values of N? Although individual sequences are
easy to compute, no one has been able to answer the general question. To put this another
way, no one knows whether the process of computing 3N+1 sequences can properly be called
an algorithm, since an algorithm is required to terminate after a finite number of steps! (This
discussion assumes that the value of N can take on arbitrarily large integer values, which is not
true for a variable of type int in a Java program.)
The point of testing is to find bugs—semantic errors that show up as incorrect behavior
rather than as compilation errors. And the sad fact is that you will probably find them. Again,
you can minimize bugs by careful design and careful coding, but no one has found a way to
avoid them altogether. Once you’ve detected a bug, it’s time for debugging . You have to
track down the cause of the bug in the program’s source code and eliminate it. Debugging is a
skill that, like other aspects of programming, requires practice to master. So don’t be afraid of
bugs. Learn from them. One essential debugging skill is the ability to read source code—the
ability to put aside preconceptions about what you think it does and to follow it the way the
computer does—mechanically, step-by-step—to see what it really does. This is hard. I can still
remember the time I spent hours looking for a bug only to find that a line of code that I had
looked at ten times had a “1” where it should have had an “i”, or the time when I wrote a
subroutine named WindowClosing which would have done exactly what I wanted except that
the computer was looking for windowClosing (with a lower case “w”). Sometimes it can help
to have someone who doesn’t share your preconceptions look at your code.
Often, it’s a problem just to find the part of the program that contains the error. Most
programming environments come with a debugger , which is a program that can help you find
bugs. Typically, your program can be run under the control of the debugger. The debugger
allows you to set “breakpoints” in your program. A breakpoint is a point in the program where
the debugger will pause the program so you can look at the values of the program’s variables.
The idea is to track down exactly when things start to go wrong during the program’s execution.
The debugger will also let you execute your program one line at a time, so that you can watch
what happens in detail once you know the general area in the program where the bug is lurking.
I will confess that I only rarely use debuggers myself. A more traditional approach to
debugging is to insert debugging statements into your program. These are output statements
that print out information about the state of the program. Typically, a debugging statement
would say something like
System.out.println("At start of while loop, N = "+ N);
You need to be able to tell from the output where in your program the output is coming from,
and you want to know the value of important variables. Sometimes, you will find that the
computer isn’t even getting to a part of the program that you think it should be executing.
Remember that the goal is to find the first point in the program where the state is not what
you expect it to be. That’s where the bug is.
And finally, remember the golden rule of debugging: If you are absolutely sure that every-
thing in your program is right, and if it still doesn’t work, then one of the things that you are
absolutely sure of is wrong.
The hstatementi can, of course, be a block statement consisting of several statements grouped
together between a pair of braces. This statement is called the body of the loop. The body
of the loop is repeated as long as the hboolean-expressioni is true. This boolean expression is
called the continuation condition, or more simply the test, of the loop. There are a few
points that might need some clarification. What happens if the condition is false in the first
place, before the body of the loop is executed even once? In that case, the body of the loop is
never executed at all. The body of a while loop can be executed any number of times, including
zero. What happens if the condition is true, but it becomes false somewhere in the middle of
the loop body? Does the loop end as soon as this happens? It doesn’t, because the computer
continues executing the body of the loop until it gets to the end. Only then does it jump back
to the beginning of the loop and test the condition, and only then can the loop end.
Let’s look at a typical problem that can be solved using a while loop: finding the average
of a set of positive integers entered by the user. The average is the sum of the integers, divided
by the number of integers. The program will ask the user to enter one integer at a time. It
will keep count of the number of integers entered, and it will keep a running total of all the
numbers it has read so far. Here is a pseudocode algorithm for the program:
Let sum = 0
Let count = 0
while there are more integers to process:
Read an integer
Add it to the sum
Count it
Divide sum by count to get the average
Print out the average
But how can we test whether there are more integers to process? A typical solution is to
tell the user to type in zero after all the data have been entered. This will work because we
are assuming that all the data are positive numbers, so zero is not a legal data value. The zero
is not itself part of the data to be averaged. It’s just there to mark the end of the real data.
A data value used in this way is sometimes called a sentinel value. So now the test in the
while loop becomes “while the input integer is not zero”. But there is another problem! The
first time the test is evaluated, before the body of the loop has ever been executed, no integer
has yet been read. There is no “input integer” yet, so testing whether the input integer is zero
doesn’t make sense. So, we have to do something before the while loop to make sure that the
test makes sense. Setting things up so that the test in a while loop makes sense the first time
it is executed is called priming the loop. In this case, we can simply read the first integer
before the beginning of the loop. Here is a revised algorithm:
Let sum = 0
Let count = 0
Read an integer
while the integer is not zero:
3.3. WHILE AND DO..WHILE 75
count = 0;
/* Read and process the user’s input. */
TextIO.put("Enter your first positive integer: ");
inputNumber = TextIO.getlnInt();
while (inputNumber != 0) {
sum += inputNumber; // Add inputNumber to running sum.
count++; // Count the input by adding 1 to count.
TextIO.put("Enter your next positive integer, or 0 to end: ");
inputNumber = TextIO.getlnInt();
}
/* Display the result. */
if (count == 0) {
TextIO.putln("You didn’t enter any data!");
}
else {
average = ((double)sum) / count;
TextIO.putln();
TextIO.putln("You entered " + count + " positive integers.");
TextIO.putf("Their average is %1.3f.\n", average);
}
} // end main()
} // end class ComputeAverage
Since the condition is not tested until the end of the loop, the body of a do loop is always
executed at least once.
For example, consider the following pseudocode for a game-playing program. The do loop
makes sense here instead of a while loop because with the do loop, you know there will be at
least one game. Also, the test that is used at the end of the loop wouldn’t even make sense at
the beginning:
do {
Play a Game
Ask user if he wants to play another game
Read the user’s response
} while ( the user’s response is yes );
Let’s convert this into proper Java code. Since I don’t want to talk about game playing at the
moment, let’s say that we have a class named Checkers, and that the Checkers class contains
a static member subroutine named playGame() that plays one game of checkers against the
user. Then, the pseudocode “Play a game” can be expressed as the subroutine call statement
“Checkers.playGame();”. We need a variable to store the user’s response. The TextIO class
makes it convenient to use a boolean variable to store the answer to a yes/no question. The
input function TextIO.getlnBoolean() allows the user to enter the value as “yes” or “no”.
“Yes” is considered to be true, and “no” is considered to be false. So, the algorithm can be
coded as
boolean wantsToContinue; // True if user wants to play again.
do {
Checkers.playGame();
TextIO.put("Do you want to play again? ");
wantsToContinue = TextIO.getlnBoolean();
} while (wantsToContinue == true);
When the value of the boolean variable is set to false, it is a signal that the loop should end.
When a boolean variable is used in this way—as a signal that is set in one part of the program
and tested in another part—it is sometimes called a flag or flag variable (in the sense of a
signal flag).
By the way, a more-than-usually-pedantic programmer would sneer at the test
“while (wantsToContinue == true)”. This test is exactly equivalent to “while
(wantsToContinue)”. Testing whether “wantsToContinue == true” is true amounts to the
same thing as testing whether “wantsToContinue” is true. A little less offensive is an expression
of the form “flag == false”, where flag is a boolean variable. The value of “flag == false”
is exactly the same as the value of “!flag”, where ! is the boolean negation operator. So
you can write “while (!flag)” instead of “while (flag == false)”, and you can write
“if (!flag)” instead of “if (flag == false)”.
Although a do..while statement is sometimes more convenient than a while statement,
having two kinds of loops does not make the language more powerful. Any problem that can be
solved using do..while loops can also be solved using only while statements, and vice versa.
In fact, if hdoSomethingi represents any block of program code, then
do {
hdoSomething i
} while ( hboolean-expression i );
hdoSomething i
while ( hboolean-expression i ) {
hdoSomething i
}
Similarly,
while ( hboolean-expression i ) {
hdoSomething i
}
can be replaced by
if ( hboolean-expression i ) {
do {
hdoSomething i
} while ( hboolean-expression i );
}
without changing the meaning of the program in any way.
the loop that contains the nested loop. There is something called a labeled break statement
that allows you to specify which loop you want to break. This is not very common, so I will
go over it quickly. Labels work like this: You can put a label in front of any loop. A label
consists of a simple identifier followed by a colon. For example, a while with a label might
look like “mainloop: while...”. Inside this loop you can use the labeled break statement
“break mainloop;” to break out of the labeled loop. For example, here is a code segment that
checks whether two strings, s1 and s2, have a character in common. If a common character is
found, the value of the flag variable nothingInCommon is set to false, and a labeled break is
is used to end the processing at that point:
boolean nothingInCommon;
nothingInCommon = true; // Assume s1 and s2 have no chars in common.
int i,j; // Variables for iterating through the chars in s1 and s2.
i = 0;
bigloop: while (i < s1.length()) {
j = 0;
while (j < s2.length()) {
if (s1.charAt(i) == s2.charAt(j)) { // s1 and s2 have a comman char.
nothingInCommon = false;
break bigloop; // break out of BOTH loops
}
j++; // Go on to the next char in s2.
}
i++; //Go on to the next char in s1.
}
The continue statement is related to break, but less commonly used. A continue state-
ment tells the computer to skip the rest of the current iteration of the loop. However, instead
of jumping out of the loop altogether, it jumps back to the beginning of the loop and continues
with the next iteration (including evaluating the loop’s continuation condition to see whether
any further iterations are required). As with break, when a continue is in a nested loop, it
will continue the loop that directly contains it; a “labeled continue” can be used to continue
the containing loop instead.
break and continue can be used in while loops and do..while loops. They can also be
used in for loops, which are covered in the next section. In Section 3.6, we’ll see that break can
also be used to break out of a switch statement. A break can occur inside an if statement,
but in that case, it does not mean to break out of the if. Instead, it breaks out of the loop or
switch statement that contains the if statement. If the if statement is not contained inside a
loop or switch, then the if statement cannot legally contain a break. A similar consideration
applies to continue statements inside ifs.
For example, consider this example, copied from an example in Section 3.2:
years = 0; // initialize the variable years
while ( years < 5 ) { // condition for continuing loop
interest = principal * rate; //
principal += interest; // do three statements
System.out.println(principal); //
years++; // update the value of the variable, years
}
The initialization, continuation condition, and updating have all been combined in the first line
of the for loop. This keeps everything involved in the “control” of the loop in one place, which
helps makes the loop easier to read and understand. The for loop is executed in exactly the
same way as the original code: The initialization part is executed once, before the loop begins.
The continuation condition is executed before each execution of the loop, and the loop ends
when this condition is false. The update part is executed at the end of each execution of the
loop, just before jumping back to check the condition.
The formal syntax of the for statement is as follows:
for ( hinitialization i; hcontinuation-condition i; hupdate i )
hstatement i
Usually, the initialization part of a for statement assigns a value to some variable, and the
update changes the value of that variable with an assignment statement or with an increment
or decrement operation. The value of the variable is tested in the continuation condition, and
the loop ends when this condition evaluates to false. A variable used in this way is called a
loop control variable. In the for statement given above, the loop control variable is years.
Certainly, the most common type of for loop is the counting loop, where a loop control
variable takes on all integer values between some minimum and some maximum value. A
counting loop has the form
for ( hvariable i = hmin i; hvariable i <= hmax i; hvariable i++ ) {
hstatements i
}
where hmini and hmax i are integer-valued expressions (usually constants). The hvariablei takes
on the values hmini, hmini+1, hmini+2, . . . , hmax i. The value of the loop control variable is
often used in the body of the loop. The for loop at the beginning of this section is a counting
loop in which the loop control variable, years, takes on the values 1, 2, 3, 4, 5. Here is an even
simpler example, in which the numbers 1, 2, . . . , 10 are displayed on standard output:
for ( N = 1 ; N <= 10 ; N++ )
System.out.println( N );
For various reasons, Java programmers like to start counting at 0 instead of 1, and they
tend to use a “<” in the condition, rather than a “<=”. The following variation of the above
loop prints out the ten numbers 0, 1, 2, . . . , 9:
for ( N = 0 ; N < 10 ; N++ )
System.out.println( N );
Using < instead of <= in the test, or vice versa, is a common source of off-by-one errors in
programs. You should always stop and think, Do I want the final value to be processed or not?
It’s easy to count down from 10 to 1 instead of counting up. Just start with 10, decrement
the loop control variable instead of incrementing it, and continue as long as the variable is
greater than or equal to one.
for ( N = 10 ; N >= 1 ; N-- )
System.out.println( N );
Now, in fact, the official syntax of a for statemenent actually allows both the initialization
part and the update part to consist of several expressions, separated by commas. So we can
even count up from 1 to 10 and count down from 10 to 1 at the same time!
for ( i=1, j=10; i <= 10; i++, j-- ) {
TextIO.putf("%5d", i); // Output i in a 5-character wide column.
TextIO.putf("%5d", j); // Output j in a 5-character column
TextIO.putln(); // and end the line.
}
As a final example, let’s say that we want to use a for loop that prints out just the even
numbers between 2 and 20, that is: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20. There are several ways to
do this. Just to show how even a very simple problem can be solved in many ways, here are
four different solutions (three of which would get full credit):
82 CHAPTER 3. CONTROL
Perhaps it is worth stressing one more time that a for statement, like any statement, never
occurs on its own in a real program. A statement must be inside the main routine of a program
or inside some other subroutine. And that subroutine must be defined inside a class. I should
also remind you that every variable must be declared before it can be used, and that includes
the loop control variable in a for statement. In all the examples that you have seen so far in
this section, the loop control variables should be declared to be of type int. It is not required
that a loop control variable be an integer. Here, for example, is a for loop in which the variable,
ch, is of type char, using the fact that the ++ operator can be applied to characters as well as
to numbers:
// Print out the alphabet on one line of output.
char ch; // The loop control variable;
// one of the letters to be printed.
for ( ch = ’A’; ch <= ’Z’; ch++ )
System.out.print(ch);
System.out.println();
3.4. THE FOR STATEMENT 83