in Java:
Architectures and Interfaces
David Schmidt
Computing and Information Science Department
Kansas State University
To the Student
As the previous narrative indicates, learning to program requires more than merely
learning to write in a particular computer language—you must understand the struc-
tures within programs and how these structures behave. To do this, you must pore
ii
over the book’s programming examples, copy them to your computer, test them, try
to “break” or “trick” them, modify them in small ways, and try them again. In many
ways, computer programs are like toys or appliances that can be examined, played
with, disassembled, and reassembled in different ways. Experiences like these help
you develop programming intuitions.
Most sections in the text end with a short exercises section that suggests simple
ways to apply and modify the programs in the section. Work at least one or two of
the exercises before you proceed to the next section, and if you have difficulty with an
exercise, do not hesitate to reread the section. It is rare for anyone to understand a
new concept after just one reading, and a technical topic like programming requires
careful, thoughtful effort for deep understanding. Remember that progress is mea-
sured by the number of concepts and structures that you can use well and not by the
number of pages you have read or number of programs you have typed.
Each Chapter concludes with a collection of projects that test your abilities to
design and build complete programs. The projects are roughly ordered in terms of
difficulty, and many are written so that you can “customize” them into a product
that you or others would enjoy using—take time to make your final result one you
would be proud to demonstrate to others.
Like video recorders and microwave ovens, programs come with instructions.
When you study the example programs in the text, pay attention to the “instructions”
(documentation) that are included. Because programs and program components are
useless without documentation, you must develop the habit of documenting the pro-
grams you write. This activity pays huge dividends when you start building large,
complex programs.
The Java programming language is used as the working language in this text.
Java is by no means perfect, but it supports the crucial structures for programming,
and it provides mechanisms for “fun” activities like graphics and animation. Chapter
2 states partial instructions for installing Java on your computer, but if you are
inexperienced at installing software, you should seek help.
The programming examples from the text and other supporting materials can be
found at the URL http://www.cis.ksu.edu/santos/schmidt/ppj.
To the Instructor
My experiences, plus the extensive feedback I have received from the Scott/Jones
reviewers and my colleagues, have caused the text to evolve into an implementation
of the following algorithm:
1. Convince the students that programs have architectures, like houses do. Tell
them programming is a learned discipline, like house design and construction.
3. Teach the students class design and component assembly via interfaces before
the students get lost in loops.
4. Use control structures and array data structures to build “smarter” objects.
and applets appear in due course. Networking is not a beginner’s topic and is
not covered.
The text does not use any specially written classes or packages supplied by the
author—only the standard Java packages are used. This prevents a beginner from
becoming dependent on nonstandard variants of Java and relieves the instructor of
the headache of installing custom packages on classroom computers and students’
personal computers.
Although the choice of Java as the text’s programming language is basically sound,
the language possesses several annoying features. One that simply cannot be avoided
is using the static method, main, to start an application’s execution. To avoid tedious
explanations of static methods and classes from which no objects are ever created,
the text naively claims in Chapter 2 that an application’s start-up class (that is, the
one that contains main) generates a “start-up” object. Technically, this is incorrect,
but it allows a beginner to stick with the axiom, “Classes Generate Objects,” which
is promoted from Chapter 1 onwards. The remainder of the text presents the syntax
and semantics of Java in technically correct detail.
The programming examples from the text and other supporting materials can be
found at the URL http://www.cis.ksu.edu/santos/schmidt/ppj.
Acknowledgements
First and foremost, I thank Gudmund Skovbjerg and his students at Aarhus Univer-
sity, Denmark, who used several earlier drafts of this text. Their comments led to
huge improvements in the book’s organization and pedagogy. Vladimiro Sassone and
his students at Catania University, Italy, and Peter Thiemann and his students at
Freiburg University, Germany, also used early drafts of the text, and they thanked
as well. I’ve also received useful comments from Thore Husfeldt and his students
at the University of Lund University, Sweden, Aake Wikstro”m and his students at
the University of Gothenburg, Sweden, and Sebastian Hunt at City University, Lon-
don. I also thank my co-instructor, William Shea, my graduate teaching assistants,
and my students at Kansas State University for tolerating numerous revisions over
a multi-year period. Bonnie Braendgaard of Aarhus University, Carolyn Schauble of
Colorado State University, and Bernhard Steffen and Volker Braun of Dortmund Uni-
versity are thanked for their insightful suggestions. I also appreciate the comments
and criticisms of my departmental colleagues, Michael Huth and Stefan Sokolowski.
Richard Jones and Robert Horan of Scott/Jones Press deserve special thanks for
their initial interest in the text, their tolerance of my rewritings, and their recruitment
of the following review team, whose commentaries led the text into its final form:
REVIEWERS’ NAMES HERE.
The book’s first draft was written while I spent part of a sabbatical year at Aarhus
University, Denmark; I thank Olivier Danvy for hosting my visit. Subsequent drafts
v
were written during periods when I was supported by the National Science Founda-
tion, the Defense Advanced Research Projects Agency, and the National Aeronautics
and Space Administration; I thank my project managers, Frank Anger (NSF), Helen
Gill (DARPA), and Mike Lowry (NASA) for tolerating what I hope will be judged a
substantial contribution to programming.
Finally, during the period of time this book was written, my mother, Frances
Louise Walters Schmidt, died; I dedicate the text to her.
vi
Table of Contents
Preface i
Index 748
Chapter 1
1.6 Summary
We begin this text by describing the fundamental aspects of computers and programs,
and we present computer programming as a three-step process:
1. designing an architecture
Most computers have also secondary storage devices—internal disk drive (“hard drive”),
compact disk drive, and diskette drive (“floppy drive”)—whose data must be copied
into the primary storage component before it can be read by the processor. Secondary
storage is designed for portability (e.g., diskettes that can be carried from one com-
puter to another) or for holding massive amounts of data, much like an accountant’s
filing cabinet can hold more information than the accountant’s scratch pad.
The filing cabinet analogy just mentioned has generated some standard computer
terminology: A file is a collection of instructions or data; files are themselves grouped
into folders (also known as directories). Directories are normally kept in secondary
storage, their contents copied into primary storage when needed by the processor.
Finally, a computer usually has several input-output devices (such as the display,
keyboard, mouse, printer), which let a human supply information to the computer
(say, by typing at the keyboard or clicking a mouse) and receive answers (by reading
it on the display or from the printer).
If a computer can execute instructions, what kind of instructions can it execute?
This depends—a conventional notebook computer of course cannot execute the in-
structions written in a cookbook, nor can it follow the instructions for driving a
car from Chicago to Manhattan. (But there are special-purpose computers that can
attempt the latter.)
The processor inside a typical computer performs a limited range of arithmetic-
like instructions—numeric addition and subtraction and text copying. But to do
even these simple tasks, numbers and text must be coded in sequences of 1’s and 0’s,
called bits. This technique of writing numbers, text, and instructions to a computer
is called binary coding. The collection of binary-coded instructions that a computer’s
processor can read and execute is called the computer’s machine language. By using
binary codings, a computer can compute with letters as well as numbers. Images
(with colors, brightness, and shapes) can also be coded with binary codings.
Exercises
1. If a computer is indeed any entity that can follow orders, then give examples
from real life of “computers.”
2. List all the input-output devices that are attached to the computer you will use
for your programming exercises.
3. Hand-held calculators are computers. What are the input-output devices for a
calculator?
the moment, forget about conventional computers—if your spouse can read and can
operate an oven, then when you write instructions for baking a cake, you are “pro-
gramming” your spouse—the instructions are a program, and you are a programmer.
The point here is that a program (in this case, for baking a cake) is a list of
instructions written in a precise style where declarative verbs (“cut,” “pour,” “heat”)
state computational actions, and nouns (“egg,” “flour,” “bowl”) state data with which
computation is performed; see Figure 1 for an example.
A sequence of declarative instructions for accomplishing a goal, like that in Figure
1, is called an algorithm. The term computer program, is used to describe an algorithm
that is written precisely enough so that a computer processor can read and execute the
instructions. For historical reasons, the writing of computer programs is sometimes
called coding, and the program itself is sometimes called code.
A computer program is almost always saved as a file on secondary storage. When
someone wants the processor to execute the program’s instructions, the programmer
starts the program—this causes the program to be copied into primary storage, where
the processor reads the instructions and executes them. Starting a program might be
done with a mouse click on a program’s icon (its picture on the display) or by typing
some text with the keyboard.
Unfortunately, computer processors execute instructions written in machine language—
the language of binary codings—and humans find this language almost impossible to
write. For this reason, other languages have been designed for programming that
are easier for humans to use. Examples of such high-level programming languages are
Fortran, Cobol, Lisp, Basic, Algol, Prolog, ML, C++, and Java. Many of these lan-
guages look like the language of secondary-school algebra augmented with a carefully
defined set of verbs, nouns, and punctuation symbols.
In this text, we use Java as our programming language.
When one writes a program in Java, there remains the problem of making the
computer’s processor understand the program. This problem is solved by the Java
designers in two stages:
into another language, called Java byte code, which is almost machine language.
The programmer starts the compiler and tells it to translate her Java program
to byte code. The resulting byte code is deposited, as a file, on secondary
storage.
2. Next, when the programmer wishes the byte code executed, she starts a second
program, the Java interpreter (also known as the Java virtual machine or JVM).
The interpreter copies the byte code to primary storage and works with the
processor to execute the machine-code versions of the byte-code instructions.
Programming languages like Java are useful for programming these kinds of ac-
tivities:
• scientific and mathematical calculations, such as calculating the roots of a
quadratic equation, drawing the curve for a polynomial, or printing a table
of monthly payments for varying interest rates on a mortgage.
• information processing, such as editing and typesetting a letter, printing a file
of paychecks, or drawing pictures on the console.
• simulation, such as imitating the cockpit of an airplane, simulating the next
five days’ weather, or playing a card game.
This text intends to show you how to use the Java language to program these activi-
ties. Along the way, you will learn practical and formal aspects of writing programs
in good style.
Exercises
1. Locate a cookbook and study one of its recipes. Mark the declarative verbs,
nouns, and precise quantities. Also, circle any instructions in the recipe that
appear to you to be imprecise.
2. Arithmetic is often called the “first programming language.” Pretend that this
expression is a program
(3 + 2) - 1 + (6 + 5)
and pretend that you are a computer. List the steps you take with your pencil
to execute the instructions in this program. (That is, compute the answer to
the expression, one step at a time.)
3. Algebra is a programming language. List the steps you take to solve the value
of x in this “program”:
3y - x = 3 + 2x
1.3. PROGRAMS ARE OBJECTS 5
DISPLAY DISKS
(Hint: the first step is to add x to both sides of the equation, producing the
new equation, 3y - x + x = 3 + 2x + x.)
4. Here is a small fragment of a Java program:
int x = 3 + 2;
int y = x + 1;
displayOnTheConsole(y);
payment schedules, etc. And a graphics program is an object that has methods for
drawing lines and geometric figures, painting the figures, moving them, and so on.
Similarly, a keyboard is an object that has methods for typing letters and numbers,
and a display is an object that has methods for displaying text, colors, and shapes. A
user has methods for typing text at a keyboard and reading answers on the display.
(Users also have methods for eating, sleeping, etc., but these are not important to
computer programming.)
Objects “communicate” with each other—one object can ask another to perform
one of its methods. This is called sending a message. The arrows in Figure 3 indicate
the directions in which messages are sent. For example, a USER might wish to
know the square root of a number, so she types the number on the KEYBOARD,
in effect sending a message to the KEYBOARD object. The KEYBOARD is the
receiver of the message. (The USER is called the client.) The KEYBOARD reacts
to the message by using one of its methods to convert key taps into a number, and
it sends a message containing the number to the EXECUTING PROGRAM. The
EXECUTING PROGRAM receives the message and uses its methods to compute
the number’s square root. The EXECUTING PROGRAM then communicates the
square root to the DISPLAY, which shows number, as symbols, on the screen so that
it reaches the USER’s eyes.
In this way, computation is performed by a series of communications between
objects. A complex executing program might itself be a collection of interconnected
objects—perhaps computing the square root of a number is completed only after
several internal communications between the program’s own executing subobjects.
Here is some commonly used terminology: When a human supplies information to
a computer program, say, by typing on a keyboard or moving a mouse, the information
is called input to the program. (Another term is input events.) Input can also be
supplied to a program from information on a disk. When the program calculates a
result or answer and this answer is displayed or saved on a disk, this is called the
program’s output. Programs are often called software, which is a pun on the term,
hardware, which of course describes physical devices such as processors, displays, and
disk drives.
Exercise
Use a computer to start a program, like a game or a word processor. List all the
windows that are created by the program, list the ways you give input information
to the program, and list the ways the program displays output.
• Finally, each method is written as a sequence of Java instructions, and the coded
methods for each class are saved as a file in secondary storage.
When the computer program is started, objects in primary storage are created from
the classes in secondary storage, forming the executing program.
This text relies heavily on one particular architecture, called the Model-View-
Controller (MVC) architecture, so we must acquire some intuition about it. To do
so, we consider an automated bank teller machine (ATM), which is a machine built
in MVC style.
When you use an ATM, you stand in front of a video screen and buttons. This
is the view of the machine. When you insert your bank card and press buttons,
you activate the ATM’s controller, which receives your bank card and button presses
and relays them into the bank, where your account is kept. The accounts inside
the bank are the model, where information about your bank account is held. The
model computes the transaction you requested and relays it back to the view—money
appears.
In summary, the ATM has these components in its architecture:
• A view object—it presents the appearance of the ATM to its human user, and it
possesses methods that receive requests (“messages”) from the user and display
results.
Many appliances are built with MVC architectures (e.g., pocket calculators, video
games, radios), and the MVC architecture adapts well to computer programs as
well—have you ever played a computer game, where the game’s view was projected
on the console screen, the mouse movements activated the game’s controller, and the
computer computed the mouse movements and showed them on the screen? The
computer game is a program with MVC architecture.
section, “Programs Are Objects”: A method is a specific computing activity that can
be activated when a message is sent to it.
Begin footnote: More precisely, when the classes are copied into primary storage,
they become objects, and the objects send messages that request the other objects to
execute the methods. End footnote
The classes, methods and the manner in which they communicate are drawn as a
class diagram.
A class diagram specifies an architecture from which a program can be constructed.
For example, if we design a program with an MVC architecture, the class diagram
displays a class that serves as the view, a class that serves as the controller, and a
class that serves as the model.
Figure 4 displays a class diagram for a simple word-processing program in MVC
architecture. The components—classes—are annotated with the methods that each
possesses; for example, class MODEL owns an insertText method, so insertText-
messages can be sent to the MODEL. When MODEL receives an insertText-message,
the instructions within the insertText method execute.
Arrows show the direction in which messages are sent. (Connections without
arrow heads suggest that messages might be sent in both directions; compare this to
Figure 2.)
Here is an informal explanation of the behavior of the architecture: When a human
user interacts with the executing word-processing program, her inputs are transferred
from the mouse and keyboard to the INPUT VIEW—perhaps mouse clicks generate
getRequest messages to the INPUT VIEW, and text typed into the edited document
generate getText messages.
As in the ATM example, messages received by the INPUT VIEW are forwarded to
the CONTROLLER component, which sends an appropriate message to the MODEL
component. The MODEL is responsible for “modelling” the document the user is
editing, so the MODEL has methods for editing and altering the document. (For
example, a getText message to the INPUT VIEW causes the CONTROLLER to
be contacted, which sends an insertText message to the MODEL. In this way, the
representation of the document within primary storage gets updated.)
The CONTROLLER also sends messages to the OUTPUT VIEW, telling it to
redisplay its presentation of the edited document on the display or to print the docu-
ment when requested. (To display or print the document, the OUTPUT VIEW must
send the MODEL a contentsOf message, asking for the current contents of the edited
document.)
Of course, this explanation of the word processor’s software architecture is infor-
mal, and additional design work is needed before Java instructions can be written for
the methods and classes.
The programming examples we encounter in the next chapter have simple one-
and two-class architectures, like one-room or two-room houses, but programs in subse-
quent chapters rise or fall on their complex designs, and we must rely on architectures
1.6. SUMMARY 11
Controller
main
1.6 Summary
Each chapter will conclude with a summary of the terms, concepts, and programming
examples that will be needed for later work. Here is the relevant material for Chapter
1:
New Terminology
• computer: an entity that executes instructions
• primary storage: the part of an electronic computer that holds the instructions
and information that the processor reads to do its work. Also known as memory,
random access memory, and RAM.
• command window: a position on the computer display where a human can type
instructions to a computer
Points to Remember
• Computer hardware and software are constructed from communicating objects.
Examples of objects are the keyboard, the display, the windows on the display,
files, and the executing program itself.
• Objects have methods, which are abilities that can be performed on request.
Objects communicate requests to other objects to perform their methods by
sending messages to them.
• Programs written in the Java language are saved in files called classes; each
class is a collection of methods. When a Java program is started, the program’s
classes are copied into primary storage; when a class is copied into primary
storage, it becomes an executing object. In this way, an executing program is
a collection of objects that send messages to one another.
In this section and in the similarly named sections in subsequent chapters, we present
material that will enhance your programming skills. This material is optional and can
be skipped on first reading.
14
Although many details are missing, we have a solid, “top level” design—an algorithm—
that we can refine.
Next, we insert details. Consider Step 3; its refinement might read as follows:
• 3.4 Repeat steps 3.2 and 3.3 until the dish is filled.
• 3.5 If the cook desires, then sprinkle grated cheese on the top.
The refinement introduces specifics about filling the dish with pasta and sauce. In
particular, notice the use of the words “Repeat ... until” in Step 3.4; this is a clever
and standard way of saying that the step of layering pasta noodles can be repeated
until a specific stopping point is reached. Similarly, Step 3.5 uses the words “if ...
then” to indicate a statement that may or may not be performed, based upon the
situation at the time the recipe is executed.
The other steps in the recipe are similarly refined, until nothing is left to chance.
If we were writing a method in the Java language, we would begin with a top-
level design, like the one just seen, and apply stepwise refinement until all instructions
were spelled out as statements in the Java language. We apply this technique to many
examples in the chapters than follow.
1.7. BEYOND THE BASICS 15
Exercises
Use stepwise refinement to write algorithms for the following:
6. how to calculate the sales tax (value-added tax) for a purchase in your commu-
nity
Customer Waiter
giveOrder calculateCheck
Cashier Cook
receiveMoney cookLasagna
cookTortellini
• Components: What are the forms of object one needs to solve the problem (in
this case, to operate the restaurant)?
• Responsibilities: What methods must each object have so that appropriate ac-
tions can be taken when there is communication? (What will the staff do when
asked?)
The answers you give to these questions should lead to an appropriate operations
plan—an architecture—for the Italian restaurant.
For the restaurant, one imagines the need for cooks, waiters, cashiers, and pre-
sumably, customers. (We will not worry about tables, chairs, pots, and pans, etc.,
for the moment.) A diagram of the components, responsibilities, and collaborations
appears in Figure 5.
Based on the diagram, we speculate that a WAITER initiates activity by sending a
giveOrder message to the CUSTOMER (that is, the WAITER asks the CUSTOMER
what she wants to eat). The CUSTOMER’s reply causes the the WAITER to send a
message to the COOK to cook the appropriate dish, e.g., cookLasagna. In response,
the COOK cooks the order, which is returned to the WAITER, who gives the food
to the CUSTOMER.
Begin Footnote: An arrow, such as WAITER --> COOK, states that the WAITER
can send a message to the COOK. The COOK is allowed to “reply” to the message
by answering with food. The point is, the COOK does not initiate messages to the
WAITER; it merely replies to the messages the WAITER sends it. End Footnote
When the CUSTOMER is finished, she sends a calculateCheck message to the
WAITER to receive the check, and a receiveMoney message is sent to the CASHIER
to pay the bill.
Computer programs can be designed this way as well, and it is easy to imagine
how a computer game might be designed—consider an “Italian-restaurant computer
game”—but even mundane information processing programs can be designed with
1.7. BEYOND THE BASICS 17
Document Clipboard
getText saveClippedText
deleteText retrieveClippedText
reformatLines
...
getHighlightedText
contentsOf Formatter
setTypeFont
getTypeFont
setLineLength
getLineLength
Exercises
Use object-oriented design to model the following:
1. a hamburger stand
2. a grocery store
3. an airport
be “constructed” from just the one class of waiter. If we review Figure 5, we note
that the four classes (WAITER, COOK, CASHIER, and CUSTOMER) can be used
to create a real restaurant with many customer objects, waiter objects, cook objects,
and cashier objects. But regardless of the number of objects built, the responsibilities
and collaboration patterns remain as indicated in the class diagram.
In addition, once we define a class, we can reuse it. For example, if yesterday we
designed a vegetable market, and the vegetable market used cashiers, then the cashier
class that we designed for the vegetable market can be reused to “build” cashiers for
the Italian restaurant project.
We use the above analogy to emphasize that classes generate objects: When we
design a Java program, we draw a class diagram that indicates the classes and their
collaborations. Next, for each class in the diagram, we write a file of Java instructions
that code the methods of the class. When the program is started, one or more objects
are created from each of the classes, and the objects are placed in primary storage.
The processor executes the instructions within the objects.
The ITALIAN CASHIER inherits (or “extends”) the CASHIER class, because it
has all the methods of the original plus the additional customizations. The large
arrowhead in the diagram denotes this inheritance.
The attractive feature of inheritance is that the customizations are not inserted
into the original class but are attached as extensions. Thus, several people can use
the one and only original class but extend it in distinct ways. (For example, the basic
CASHIER class might be extended into an Italian restaurant cashier and extended
also into a bank cashier. For that matter, a restaurant might be built to have one
CASHIER object (who does not speak Italian) and one ITALIAN CASHIER object
(who does)).
1.7. BEYOND THE BASICS 19
If we must write a simulation of a jungle, it makes sense to design one basic animal
class, design a mammal class that inherits it, design a feline class that inherits it, etc.
This gives us a coherent and economical design of the jungle animals. Also, if we
wish to add birds, zebras, and other new animals later, we can easily integrate the
new classes into the design.
Chapter 2
2.6 Summary
This chapter applies concepts from Chapter 1 to building simple programs in the
Java language. Its objectives are to
• present the standard format of a Java program and illustrate the instances of
class, method, and object, as they appear in Java.
• explain the steps one takes to type a program, check its spelling and grammar,
and execute it
• show how an object can send messages to other objects and even create other
objects as it executes.
System.out
println [Note: Java’s name for the command window]
Hello to you!
49
To make the text appear, our application sends messages to a pre-existing object,
named System.out, which is Java’s name for the command window. The application
we build is named Hello; it interacts with System.out in the pattern portrayed in the
class diagram of Figure 1.
The class diagram shows that Hello has a main method; a message from the
“outside world” to main starts the application. The other component, System.out,
has its name underlined to indicate that it is a pre-existing object—a Java object that
is already connected to the command window. The object has a method, println
(read this as “printline”), which knows how to display a line of text in the command
window. The Java designers have ensured that a System.out object is always ready
and waiting to communicate with the applications you write.
22
The arrow in the diagram indicates that Hello sends messages to System.out.
That is, it uses System.out by communicating with (or “connecting to”) its println
method.
Using terminology from Chapter 1, we say that Figure 1 presents an architecture
where Hello is the controller (it controls what the program does) and System.out is
the output view (it displays the “view” of the program to the human who uses it).
We write and place the instructions for class Hello in a file named Hello.java.
Java instructions look like “technical English,” and the contents of Hello.java will
look something like this:
public class Hello
{ public static void main(String[] args)
{
... to be supplied momentarily ...
}
}
Java is a wordy programming language, and we must tolerate distracting words like
public, static, void, which will be explained in due course. For now, keep in mind
that we are writing a class that has the name Hello and contains a method named
main. The set braces act as “punctuation,” showing exactly where a method and a
class begin and end. The ellipses indicate where the instructions will be that send
messages to System.out.
For our example, main must contain instructions that display two full lines of text.
The algorithm for this task goes:
1. Send a message to System.out to print ”Hello to you!” on a line of its own in
the command window.
2. Send a message to System.out to print the number 49 on a line of its own in
the command window.
We must convert the above algorithm into Java instructions and insert them where
the ellipses appeared above. Step 1 of the algorithm is written like this in Java:
System.out.println("Hello to you!");
This instruction sends to System.out the message to print-line (println) the text,
"Hello to you!". In similar fashion, Step 2 is written
System.out.println(49);
The technical details behind these two instructions will be presented momentarily;
for now, see Figure 2 for the completely assembled program.
Before we dissect Figure 2 word by word, we demonstrate first how one types the
program into the computer, checks the program’s spelling and grammar, and starts
it.
2.2. HOW TO BUILD AND EXECUTE AN APPLICATION 23
2. The program’s spelling and grammar must be checked by the Java compiler;
that is, the program must be compiled.
To perform these steps, you use either your computer’s (i) integrated development
environment (IDE) for the Java language, or (ii) text editor and the Java Development
Kit (JDK).
We briefly examine both options, and you should obtain help with selecting the
one you will use. If you are not interested in this selection at this moment in time,
you may skip either or both of the two subsections that follow.
Next, type the class into the window and save it by using the IDE’s Save button:
Next, the program must be compiled. Compile by selecting the IDE’s Compile or
26
Build button:
If there are any spelling or grammar errors, they will be listed in a small window
of their own; otherwise, a message will announce that the compile has completed
successfully. In the former case, you repair the errors and try again; in the latter case,
you will see that the compiler has created the translated version of the application
and placed it in the file, Hello.class.
Finally, start the program by selecting the button named Run or Launch (or Start,
etc.) This starts the Java interpreter, which helps the processor read and execute
2.2. HOW TO BUILD AND EXECUTE AN APPLICATION 27
the byte-code instructions in Hello.class. In the case of the Hello application, its
execution causes two full lines of text to appear in the command window. The IDE
shows the command window when needed:
(Footnote: Unfortunately, your IDE might show and remove the command window
before you can read the text that appeared! If the command window disappears
too quickly, insert these lines into your Java program immediately after the last
28
System.out.println:
try { Thread.sleep(5000); }
catch (InterruptedException e) { }
These cryptic extra lines delay the program by 5 seconds, giving you time to read the
contents of the command window. EndFootnote.)
This starts the Java compiler, which examines the application, line by line, attempting
to translate it into byte-code language. If the program is badly formed—there are
spelling or grammar or punctuation errors—then the compiler will list these errors.
If there are errors, correct them with the text editor and compile again; if not, then
you will see that the Java compiler created the translated version of the application,
a byte-code file named Hello.class.
To execute the program, type the command, java Hello. This starts the program,
more specifically, it starts the Java interpreter that helps the processor execute the
30
byte-code instructions, and the two lines of text appear in the command window:
Details about error messages and error correction appear in a section later in this
Chapter.
Exercise
Install either an IDE or the JDK on your computer and type, compile, and execute
the program in Figure 2.
Begin Footnote: By the way, it confuses the Java compiler if you place one com-
ment inside another, e.g.
so do not do this! Also, the Java compiler will accept comments that begin with /* as
well as /**, but we use the latter for reasons explained in Chapter 5. End Footnote.
Following the comment is the line that gives the class’s name, and then there are
two matching brackets:
The line with the program’s name is the program’s header line. Hello is of course the
name, but the words public and class have special, specific meanings in Java.
Words with special, specific meanings are called keywords. The keyword, public,
indicates that the class can be used by the “general public,” which includes other
objects and human users, to build objects on demand. The keyword, class, indicates
of course that Hello is a Java class, the basic unit of program construction.
Following the program’s header line is the program’s body, which is enclosed by
the matching brackets, { and }. Within the body lie the program’s methods (as well
as other items that we encounter in later chapters).
In the examples in this text, we will align the brackets vertically so that they are
easy to see and match. The only exception to this rule will be when the brackets
enclose a mere one-line body; then, the closing bracket will be placed at the end of
the same line, to save space on the printed page. (See Figure 2 again.) When you
type your own Java programs, you might prefer to use this style of bracket placement:
because it is easier to use with a text editor. (But this style makes it easy to forget a
bracket, so be careful.) Do as your instructor indicates; we will not discuss this issue
again.
Back to the example—Hello’s body has but one method, main, which has its own
header line and body:
Method main is surrounded by a slew of keywords! The word, public, means the same
as before—the main method may receive messages from the general “public.” (This
includes the user who starts Hello and wants main to execute!) The keyword, static,
refers to a subtle, technical point, and its explanation, along with that of void and
(String[] args), must be postponed.
The body of the main method contains the instructions (statements) that will be
executed when a Hello object is sent the message to execute its main method. These
instructions are
System.out.println("Hello to you!");
System.out.println(49);
Exercises
1. Write a Java program that displays your name on one line.
2.3. HOW THE APPLICATION WORKS 33
2. Write a Java program that prints your name, where your first (given) name is
on a line by itself, and you last (family) name is on a line by itself.
3. For both of the previous exercises, explain why your program has the architec-
ture presented in Figure 1. (This makes clear that different programs can be
built in the same architectural style.)
Objects rest in primary storage and wait for messages. So, to start the computa-
tion, the computer (more precisely, the JVM) sends Hello a message to start its main
method.
When Hello receives the message, it indeed starts main, whose statements are
executed, one by one. We use a marker, >, to indicate which statement is executed
first:
Hello System.out
...
public static void main(String[] args)
println(...)
{ > System.out.println("Hello to you!");
{ instructions to print text }
System.out.println(49);
}
34
Eventually, System.out fulfills its request and the text, Hello to you! appears in the
command window. Once this happens, System.out signals Hello that it can proceed
to its next statement, and System.out returns to a “resting” state:
Hello System.out
...
public static void main(String[] args)
println(...)
{ ...
{ instructions to print text }
> System.out.println(49);
}
Next, another message is sent to System.out, requesting that its println method dis-
play the number 49. (Notice that double-quote marks are not used around numbers.)
Again, Hello waits until System.out executes its message and signals completion:
Hello System.out
...
public static void main(String[] args)
println(...)
{ ...
{ > instructions to print text }
> AWAIT System.out’s COMPLETION
}
Exercise
Write an execution trace for a program you wrote from the earlier Exercise set.
2.4. HOW ONE OBJECT CONSTRUCTS ANOTHER 35
System.out
println
print
Finished
That is, the programmer’s name, Fred Mertz, is printed followed by the exact moment
that the program executes. Then an empty line and a line consisting of just Finished
appears.
Figure 3 has an interesting architecture: NameAndDate is the controller, because
it controls what will happen; System.out is the output view, because it presents the
“view” of the program to its user; and GregorianCalendar is the model, because it
“models” the computer clock—this is our first, simplistic example of a model-view-
controller (MVC) architecture—see Chapter 1.
We must write the controller, class NameAndDate. The controller’s main method
asks System.out to print the text in the command window, but main must also ask
36
a GregorianCalendar object for the exact time. To do this, main must construct the
object first before it sends it a message. Here are the crucial steps main must do:
• Construct a new GregorianCalendar object, by saying
new GregorianCalendar()
The keyword, new, constructs an object from the class named GregorianCalendar.
The matching parentheses, ( and ), can hold extra arguments if necessary; here,
extra arguments are unneeded to construct the object.
• Give the newly created object a variable name so that we can send messages to
the object:
c.getTime()
System.out.println(c.getTime());
we must provide some background: The Java language comes with many prewritten
classes for your use. The classes are grouped into packages, and the package where one
finds class GregorianCalendar is java.util. The statement, import java.util.*,
tells the Java interpreter to search in the java.util package, where it will locate
class GregorianCalendar. Once the class is located, an object may be constructed
2.4. HOW ONE OBJECT CONSTRUCTS ANOTHER 37
from it. It will always be clear in this text’s examples when a special statement like
import java.util.* is necessary.
Begin Footnote: Stated more precisely, when a class, C, is mentioned in a state-
ment, the Java interpreter must locate the file, C.class. Normally, the interpreter
searches in two places: (1) the folder in which the application was started, and (2)
java.lang, a package of general-purpose classes. If C.class does not reside in either
of these two places, an import statement must indicate where else the interpreter
should search. End Footnote
The first statement within main says
System.out.print("Fred Mertz --- ");
was explained above: a new object is constructed from class GregorianCalendar and is
named c. the reason for the apparently redundant leading word, GregorianCalendar,
is best explained in the next chapter; for the moment, we note that the leading word
states that c is a “type” of name only for GregorianCalendar objects.
Variable names are developed fully in the next chapter, and our use of one to
name for the GregorianCalendar object is merely a “preview.”
The statement,
System.out.println(c.getTime());
has been explained; the date that is computed and returned by the GregorianCalendar
object becomes the argument to the println message, and therefore it is the date (and
not the message itself) that is displayed in the command window. We see this behavior
explained in the execution trace that follows.
Finally,
System.out.println()
Execution Trace
Because this example is an important one, we study its execution trace. The instant
after the NameAndDate application is started, primary storage looks like this:
NameAndDate System.out
main print(...)
{ > System.out.print("Fred Mertz --- "); { instructions to print text }
GregorianCalendar c = new GregorianCalendar();
System.out.println(c.getTime()); println(...)
System.out.println(); { instructions to terminate text }
System.out.println("Finished");
}
Once the text, Fred Mertz ---, is printed, the interpreter begins the next state-
ment, which creates a new GregorianCalendar object—a segment of primary storage
is allocated to hold the contents of a GregorianCalendar object, as described by class
GregorianCalendar.
Begin Footnote: More specifically, the file, GregorianCalendar.class is used to
construct the object in storage. End Footnote
2.4. HOW ONE OBJECT CONSTRUCTS ANOTHER 39
NameAndDate System.out
main
... as before ...
{ ... // the test, "FredMertz --- " has been displayed
> GregorianCalendar c = new GregorianCalendar();
System.out.println(c.getTime());
System.out.println();
System.out.println("Finished");
}
a1 : a GregorianCalendar object
...
getTime()
{ a method that reads the clock and returns the time }
The new object has an internal address where it is found. Here, the address of the
newly constructed GregorianCalendar object is a1. The object at address a1 has its
own internal structure, including a method named getTime.
Next, a storage space (called a cell) is created, and the address is placed into the
cell. In this way, c is made to name the object at address a1:
NameAndDate System.out
main
... as before ...
{ ... // the test, "FredMertz --- " has been displayed
GregorianCalendar c == a1
> System.out.println(c.getTime());
System.out.println();
System.out.println("Finished");
}
a1 : a GregorianCalendar object
...
getTime()
{ a method that reads the clock and returns the time }
Now, a println message to System.out must be sent. But the message’s argument
is not yet determined. For this reason, the interpreter sends a getTime() message to
c, and since c names a cell that holds address a1, the object at address a1 is sent the
40
a1 : a GregorianCalendar object
...
getTime()
{ > a method that reads the clock and returns the time }
a1 : a GregorianCalendar object
...
getTime()
{ a method that reads the clock and returns the time }
After the time is placed in the position where it was requested, it becomes the
argument part of the println message that is sent to System.out. This is a pattern
that appears often in Java statements: When part of a statement is written within
parentheses, the parenthesized part executes first, and the result, if any, is deposited
in the parentheses.
System.out displays the date and time, and execution proceeds to the last two
statements in main.
Finally, we note that only one message is sent to the GregorianCalendar object
that is constructed by the application. In the case that an object is constructed and
just one message is sent to it immediately thereafter, we need not give the object a
name. Here is the application rewritten so that the object’s name no longer appears:
import java.util.*;
/** NameAndDate prints my name and the exact date and time. */
2.5. REPAIRING COMPILER ERROR MESSAGES 41
System.out.println(new GregorianCalendar().getTime());
embeds within it the steps of (i) constructing a new GregorianCalendar object (ii)
immediately sending the object a getTime() message, and (iii) using the message’s
reply as the argument to System.out.println. This complex arrangement executes
correctly because the Java interpreter executes phrases within parentheses first.
Exercise
Revise Figure 3 so that it prints your own name with the date and time. Compile
and execute it. Execute it again. Compare the outputs from the two executions.
Perhaps we save this program in Test.java and compile it. The compiler replies with
several error messages, the first of which is
This message states there is an error in Line 2 of the program, at approximately the
position marked by the caret. The explanation of the error is not so helpful, but
its position is important because we can compare the above to a correctly written
program and notice that we forgot the keyword, void.
The compiler reports an error on the next line, also:
Again, the compiler’s message is not so helpful, but the position of the error suggests
there is something wrong with the word, Hello!—we forgot to enclose the string
within double quotes. Again, we can spot this error quickly by comparing our println
statement to one in the text that was correctly written.
There is also an error in the following line:
This time, the explanation is on target—we are indeed lacking a closing bracket.
Finally, the compiler reports an error at Line 1:
The compiler’s message is correct—the name of the class must be spelled exactly the
same as the name of the file in which the class is saved. Therefore, we should change
the first line to read public class Test.
Once we repair the errors, we have this program:
When we compile the revised program, the compiler identifies one error we missed:
Indeed, we have forgotten to end the statement with a semicolon. Once we repair
this last error, the compiler will successfully compile our program. (Unfortunately, a
compiler is not perfect at detecting all grammar errors on a first reading, and it is
2.6. SUMMARY 43
common that an highly erroneous program must be compiled several times before all
its grammar errors are exposed.)
Although the Java compiler will methodically locate all the grammar errors in an
application, a programmer should not rely on it as a kind of “oracle” that announces
when an application is ready to be executed. Indeed, a programmer should be familiar
with the appearance (syntax) and meanings (semantics) of the statements in the Java
language. An introduction to these notions appears in the sections at the end of this
chapter, and Appendix I provides a thorough description of the syntax and semantics
for the subset of the Java language used in this text.
Finally, remember that the compiler’s role is to enforce spelling and grammar and
not to comment on the suitability of the statements in the program. For example,
the English sentence, “Turn left into the Atlantic Ocean, and drive on the ocean floor
until you reach the east coast of France”, is a grammatically correct but procedurally
and geographically dubious instruction for someone who wishes to travel from New
York City to Paris! In a similar way, a program can contain grammatically correct
but dubious statements.
For this reason, the Java compiler can not guarantee that a program will perform
the actions that its author intends. What the author intends lives in her brain; how she
converts these intentions into written instructions cannot be checked by the compiler!
For this reason, we must study design, testing, and validation techniques that help
a programmer correctly convert her intentions into programs. We encounter these
techniques in subsequent chapters.
2.6 Summary
We conclude the chapter with a summary of the new constructions, terms, and con-
cepts.
New Constructions
• Java class (from Figure 2):
System.out.println("Hello to you!");
new GregorianCalendar()
New Terminology
• application: a Java program that is started by a human; an object is constructed
in primary storage and a message is sent to the object to execute its main
method.
• main method: the “start up” method that executes first when an application is
started.
• comment: an explanation inserted into a program for a human (and not the
computer) to read.
import java.util.*;
public class ExampleOfImportation
{
... new GregorianCalendar() ...
}
• class GregorianCalendar (a class from which one can construct objects that
read the computer’s clock). Found in the package, java.util. Method:
into a main method. What happens when you execute the program?
4. Modify the program from the previous exercise as follows: Insert between the
two statements this Java instruction:
try { Thread.sleep(5000); }
catch (InterruptedException e) { }
We will not dissect this complex statement—simply stated, it causes the main
method to delay for 5000 milliseconds (that is, 5 seconds). Compile and execute
the program. What happens?
5. Write the smallest Java program (fewest number of characters) that you can.
Is there a largest possible (most number of characters) program?
/\
/ \
----
| - |
| | ||
2.8. BEYOND THE BASICS 47
2.8.2 Semantics
Here is optional, supplemental material that will bolster your understanding of the
concepts in this chapter.
2.8.1 Syntax
When we discuss the appearance of a program’s statements—spelling, use of blanks,
placement of punctuation—we are discussing the program’s syntax. The Java compiler
strictly enforces correct syntax, and we have no choice but to learn the syntax rules
it enforces.
Appendix I gives a precise description of the syntax for the subset of the Java
language we use in this text. The description is precise, detailed, and a bit tedious.
Nonetheless, it is important that we learn to read such definitions, because they tell
us precisely what grammatically correct Java programs look like.
For exercise, we present here the part of the syntax definition that matches the
examples seen in this chapter. (Note: because they are developed more fully in the
next chapter, we omit the presentation of variable names here.)
Class
A Java class has this format:
The above is an equation, called a syntax rule or a BNF rule. Read the equation,
CLASS ::= ... as stating, “a well-formed CLASS has the format ...”.
We see that a well-formed CLASS begins with the keywords, public and class,
followed by an entity called an IDENTIFIER, which we describe later. (The IDENTIFIER
is the class’s name, e.g., NameAndDate in Figure 4. For now, think of an IDENTIFIER
as a single word.)
Following the class’s name is a left bracket, {, then zero or more entities called
METHODs, defined momentarily. (The * should be read as “zero or more.”) A class is
concluded with the right bracket, }.
The classes in Figures 2 and 4 fit this format—both classes hold one method, main.
48
Method
A method, like the main method, can have this format:
METHOD ::= public static void METHOD_HEADER
METHOD_BODY
That is, following the words, public static void, a METHOD HEADER (the method’s
name) and METHOD BODY (its body) appear.
METHOD_HEADER ::= IDENTIFIER ( FORMALPARAM_LIST? )
The METHOD HEADER has a format consisting of its name (e.g., main), followed by
a left bracket, followed by an optional FORMALPARAM LIST. (The ? means that the
FORMALPARAM LIST can be absent.) Then there is a right bracket.
In this chapter, the FORMALPARAM LIST was always String[] args, but we study
other forms in a later chapter.
METHOD_BODY ::= { STATEMENT* }
Statement
There are several statement forms in Java; the form we used in Chapter 2 is the
message-sending statement, called an INVOCATION. The format looks like this:
STATEMENT ::= INVOCATION ;
INVOCATION ::= RECEIVER . IDENTIFIER ( ARGUMENT_LIST? )
That is, an INVOCATION has a format that first lists the RECEIVER, which is the name
of an object (e.g., System.out). A period follows, then comes an IDENTIFIER, which
is the method name (e.g., print). Finally, there are matching brackets around an
optional ARGUMENT LIST, defined later.
The syntax rule for well-formed RECEIVERs is instructive:
RECEIVER ::= IDENTIFIER
| RECEIVER . IDENTIFIER
| OBJECT_CONSTRUCTION
We read the vertical bar, |, as “or.” That is, there are three possible ways of writing
a RECEIVER: The first is just a single IDENTIFIER, e.g., System is a RECEIVER; the second
way is an existing receiver followed by a period and an identifier, e.g., System.out.
The third way is by writing an OBJECT CONSTRUCTION, e.g., new GregorianCalendar;
see below.
Notice how the recursive (self-referential) definition of RECEIVER gives a terse and
elegant way to state precisely that a RECEIVER can be a sequence of IDENTIFIERs
separated by periods.
2.8. BEYOND THE BASICS 49
Object Construction
These constructions will be developed more carefully in the next chapter; for the mo-
ment, we state that a LITERAL consists of numbers, like 12 and 49, and strings, which
are letters, numerals, and punctuation enclosed by double quotes. The IDENTIFIERs
used in Chapter 2 were sequences of upper- and lower-case letters.
2.8.2 Semantics
The syntax rules in the previous section tell us nothing about what a Java application
means (that is, what the application does). When we discuss a program’s meaning,
we are discussing its semantics. From the examples in this chapter, we learned the
informal semantics of a number of Java constructions. As a exercise, we repeat this
knowledge here. The subsections that follow are organized to match the similarly
named subsections of syntax rules in the previous section, and by reading both in
parallel, you can gain a systematic understanding of the syntax and semantics of the
Java constructions used in this Chapter.
50
Class
Given a class, public class IDENTIFIER { METHOD* }, a user starts the class as an
application by typing a class’s name, that is, the IDENTIFIER part. The file with the
name IDENTIFIER.class is located, and an object is constructed by copying the file
into primary storage.
Begin Footnote: As noted earlier in the Chapter, this explanation deliberately
avoids technicalities regarding invocation of so-called static methods of classes. The
issue will be handled later. End Footnote
A message is sent to the newly constructed object’s main METHOD.
Method
When an object receives a message, it must identify which method is requested by
the message. The object extracts from the message the method name, IDENTIFIER,
and it locates the METHOD whose METHOD HEADER mentions the same IDENTIFIER:
(At this time, we cannot discuss the use of the FORMALPARAM LIST and we skip this.
In a later chapter, we learn that the ARGUMENT LIST information that is attached to a
message “connects” or “binds” to the FORMALPARAM LIST.)
The statements in the METHOD BODY are executed, one by one, in order. It is possible
for the last statement in the METHOD BODY to “reply” with an “answer” to the message.
We study this behavior in a later chapter.
Statement
An invocation, RECEIVER . IDENTIFIER ( ARGUMENT LIST? ), sends a message to the
object named RECEIVER, telling it to execute its method named IDENTIFIER. The
optional ARGUMENT LIST states additional details that help the method do its job. If
the RECEIVER’s method, IDENTIFIER, is equipped to “reply” with an “answer,” then
the answer is inserted at the very position in the program where the message was
sent.
Object Construction
The phrase, new IDENTIFIER ( ARGUMENT LIST? ), constructs an object in primary
storage from the file, IDENTIFIER.class. The optional ARGUMENT LIST lists information
that aids in the construction. The construction step generates an internal “address”
that names the newly constructed object, and this address is treated as a “reply”
similar to that described in the previous subsection.
2.8. BEYOND THE BASICS 51
at the API’s starting Web page, which looks something like the following:
We recall that System.out lives in the java.lang package, so we search down the list
of packages to find and select the link named java.lang. (If you do not know the
package where a component lives, you can always use the alphabetized index.)
2.8. BEYOND THE BASICS 53
When we reach the Web page for java.lang, we see a description of the package:
The components of the package are listed, and one them is named System. By selecting
54
Among the System’s components, we find and select the one named out, and we end
2.8. BEYOND THE BASICS 55
our investigation by viewing the details for out (that is, System.out):
In this way, you can browse through the Java packages and learn their contents.
(As an exercise, you should consult the Web page for java.util and locate class
GregorianCalendar; you will see that it has many more methods than just the getTime
method we used so far.)
Chapter 3
3.4 Booleans
3.11 Summary
means that the multiplications and additions are computed to 251 before System.out
is told to print.
If a comment, like the one in the above example, extends across multiple lines, we
begin each new line with an asterisk; the reason is explained in Chapter 5.
In the Java language, whole numbers, like 6, 0, 251, and -3, are called integers.
We see momentarily that the Java language uses the keyword, int, to designate the
collection of integers.
Begin Footnote: More precisely, int is Java’s name for those integers that can be
binary coded within one computer word, that is, the integers in the range of -2 billion
to 2 billion. End Footnote.
Of course, you can write Java programs to do other numerical calculations. For
the moment, consider addition (+), subtraction (-), and multiplication (*); you can
use these in a Java program the same way you write them on paper, e.g.,
1 + ((2 - 4) * 3) + 5
is a Java arithmetic expression that calculates to 0. You can test this example simply
enough:
public class Test
{ public static void main(String[] args)
{ System.out.println(1 + ((2 - 4) * 3) + 5); }
}
When the computer executes this program, it calculates the expression from left to
right, respecting the parentheses; if we could peer inside the computer’s processor,
we might see these steps:
1 + ((2 - 4) * 3) + 5
=> 1 + ((-2) * 3) + 5
=> 1 + (-6) + 5
=> -5 + 5 => 0
When you write arithmetic expressions, insert parentheses to make clear the order in
which operations should occur.
Exercises
Calculate each of these expressions as the computer would do; show all the calculation
steps. After you have finished the calculations, write each of them within a Java
application, like the one in this section.
1. 6 * ((-2 + 3) * (2 - 1))
2. 6 * (-2 + 3) * (2 - 1)
3. 6 * -2 + 3 * (2 - 1)
4. 6 * -2 + 3 * 2 - 1
3.2. NAMED QUANTITIES: VARIABLES 59
int quarters = 9;
The keyword, int, signifies that quarters names an integer; indeed, it names 9.
This form of statement is called a variable declaration; more precisely, it is a vari-
able initialization, because it creates a new variable and initializes it with a numerical
value. The keyword, int, is a data type—it names a “type” or “species” of data
value, namely the integers. The keyword, int, tells the Java compiler that the value
of quarters, whatever it might be, must be from the data type int.
Since the variable, quarters, names an integer, we use it just like an integer, e.g.,
System.out.println(quarters * 25);
would calculate and print 225. When we use a variable like quarters, in an expression,
we say we are referencing it.
Indeed, when the Java compiler examines a reference to a variable, like the one in
the previous statement, it verifies that the variable’s data type (here, int) is accept-
able to the context where the variable is referenced (here, the variable is multiplied by
25—this is an acceptable context for referencing variable quarters). This examina-
tion by the Java compiler, called data-type checking—prevents problems which might
arise, say, if an int-typed variable was referenced in a context where a string was
expected.
When we need to distinguish between variables, like quarters, that denote inte-
gers, and actual integers like 6 and 225, we call the latter literals.
The name you choose for a variable is called an identifier; it can be any se-
quence of letters, numerals, or the underscore, , or even a dollar sign, $ (not rec-
ommended!), as long the name does not begin with a numeral, e.g., quarters or
QUArTers or my 4 quarters. But Java keywords, like public and class, cannot be
used as variable names.
60
The program itself appears in Figure 1. Note that long statements, like the last
one in the Figure, can extend to multiple text lines, because the semicolon marks a
statement’s end.
It is easy to see the four variables and understand how they are referenced for
change calculation. Now, if you wish to modify the program and calculate a total
for different quantities of coins, all you must do is change the appropriate variable
initializations, and the remainder of the program works correctly.
3.2. NAMED QUANTITIES: VARIABLES 61
We finish the change calculation example with this last improvement: We make
the total print as dollars and cents, like so:
For these quantities of coins:
Quarters = 9
Dimes = 2
Nickels = 0
Pennies = 6
The total is 2 dollars and 51 cents
The secret to converting a cents amount to dollars-and-cents is: divide the cents
amount by 100, calculating how many whole dollars can be extracted—this is the
quotient of the result. In the Java language, an integer quotient is computed by the
integer-division operator, /. For example,
because 100 can be extracted from 251 at most 2 times (and leaving a nonnegative
remainder).
The remainder part of an integer divison is calculated by the remainder (“mod-
ulo”) operator, %:
because after groups of 100 are extracted from 251 as many as possible, 51 remains
as the leftover.
To use this technique, we write these statements:
int total = (quarters * 25) + (dimes * 10) + (nickels * 5) + (pennies * 1);
System.out.print("The total is ");
System.out.print(total / 100);
System.out.print(" dollars and ");
System.out.print(total % 100);
System.out.println(" cents");
The variable initialization lets total name the total of the change, and the statements
that follow apply the quotient and remainder operations to total. These statements
print
The total is 2 dollars and 51 cents
The long sequence of print statements just seen can be compressed into just one
with this Java “trick”: When a textual string is “added” to another string or an
integer, a longer string is formed. For example
System.out.println("Hello" + "!");
System.out.println("Hello" + (48 + 1));
62
Hello!
Hello49
Figure 2 exploits everything we have learned in the final version of the change-
calculation program.
Exercises
1. Modify the application in Figure 1 so that it calculates the value of 3 quarters
and 12 nickels; compile it and execute it.
3.2. NAMED QUANTITIES: VARIABLES 63
2. Modify the application in Figure 1 so that it displays the value of each group
of coins and then the value of all the coins. Try the application for 4 quarters,
1 nickel, and 1 penny. The application should reply:
Explain what happens when the modified application is used with the quantities
of coins listed in the previous Exercise. (We will repair the difficulty in the
section, “Converting between Strings and Numbers and Formatting.”)
The diagram shows that each number is saved in a cell. When a statement like
System.out.println("Quarters = " + quarters) is executed, the reference to quarters
causes the computer to look inside the cell named by quarters and use the integer
therein.
The above picture is important, because the Java language allows you to change
the value held in a variable’s cell; you use a statement called an assignment. Here is
a small example, where a variable, money, is initialized to 100, and then the money is
completely withdrawn:
int money = 100;
System.out.println(money);
money = 0;
System.out.println(money);
The statement, money = 0, is the assignment; it alters the value within the variable
cell that already exists. This sequence of statements displays in the command window,
100
0
because the cell’s initial value, 100, was overwritten by 0 by the assignment.
A more interesting example creates a variable to hold money and then deposits
50 more into it:
int money = 100;
System.out.println(money);
money = money + 50;
System.out.println(money);
First, 100 is printed, and then the assignment, money = money + 50 calculates a new
value for money’s cell, namely, the previous value in the cell plus 50. Thus, 150 prints
the second time the value in money’s cell is consulted.
We use the power of assignments to write another classic program with change:
Say that we desire a program that prints the coins one needs to convert a dollars-and-
cents amount into coins. For example, if we have 3 dollars and 46 cents, the program
reports that we receive this change:
quarters = 13
dimes = 2
nickels = 0
pennies = 1
If you solved this problem on a street corner, you would first count the number
of quarter-dollar coins that are needed and subtract the amount of quarters from the
starting money (e.g., 3.46 - (13*0.25) = 0.21). Then, you would repeat the process
for dimes (0.21 - (2*0.10) = 0.01), nickels (0.01 - (0*0.5) = 0.01) and pennies. The
value of the remaining money decreases from 3.46 to 0.21 to 0.01 to 0.
Here is the algorithm for the change-making method:
3.2. NAMED QUANTITIES: VARIABLES 65
2. Subtract the maximum number of quarters from money, and print the quantity
of quarters extracted.
3. Subtract the maximum number of dimes from money, and print the quantity of
dimes extracted.
4. Subtract the maximum number of nickels from money, and print the quantity of
nickels extracted.
2. The calculation of the division, money / 25, does not alter the value in money’s
cell, which remains 346—only an assignment statement can change the value in
money’s cell.
The second statement, money = money % 25, deals with the second point just men-
tioned: Since we have calculated and printed that 13 whole quarters can be extracted
from the amount of money, we must reset the value in money’s cell to the remainder.
This can be done either of two ways:
66
1. By the statement,
which calculates the monetary value of the extracted quarters and subtracts
this from money.
• 14 % 3 computes to 2
• 6 % 3 computes to 0
• 4 % 5 computes to 4
The combination of the integer quotient and remainder operations calculates the
correct quantity of quarters. We apply this same technique for computing dimes and
nickels and see the resulting program in Figure 3.
3.2. NAMED QUANTITIES: VARIABLES 67
When the application starts, the main method executes, and the three variable
initializations create three cells:
MakeChange
main
{ int dollars == 3
int cents == 46
int money == 346
> System.out.println("quarters = " + (money / 25));
money = money % 25;
> System.out.println("dimes = " + (money / 10));
money = money % 10;
...
}
At this point, quarters = 13 is printed, because 346 / 25 gives the quotient, 13.
The assignment, money = money % 25, that follows causes the value in money’s cell to
decrease, because money % 25 computes to 21:
MakeChange
main
{ int dollars == 3
int cents == 46
int money == 21
...
> System.out.println("dimes = " + (money / 10));
money = money % 10;
...
}
The value in money’s cell changes twice more, as dimes and nickels are extracted from
it.
The circular-appearing assignments in the example, like money = money % 25, might
distress you a bit, because they look like algebraic equations, but they are not. The
semantics of the previous assignment statement proceeds in these three steps:
1. The variable cell named by money is located
2. The expression, money % 25, is computed to an answer, referencing the value
that currently resides in money’s cell.
3. The answer from the previous step is placed into money’s cell, destroying what-
ever value formerly resided there.
It is unfortunate that the equals sign is used to write an assignment statement—in
Java, = does not mean “equals”!
Finally, because of the existence of the assignment statement, it is possible to
declare a valueless variable in one statement and assign to it in another:
68
but if possible, declare and initialize a variable in one and the same statement.
Exercises
1. Revise the program, in Figure 3 so that it makes change in terms of five-dollar
bills, one-dollar bills, quarters, dimes, nickels, and pennies.
2. What are the results of these expressions? 6/4; 6%4; 7/4; 7%4; 8/4; 8%4; 6/-4;
-6%4; 6%-4.
3. Use algebra to explain why the assignment, money = money % 25, in the MakeChange
program correctly deducts the number of a quarters from the starting amount
of money.
f = (9.0/5.0)*c + 32
3.3. ARITHMETIC WITH FRACTIONS: DOUBLES 69
Once we decide on a value for c, the degrees Celsius, we can calculate the value of
f, the degrees in Fahrenheit. So, if c is 22, we calculate that f is 71.6, a fractional
number. In Java, fractional numbers are called doubles (“double-precision” fractional
numbers). The Java keyword for the data type of doubles is double.
Double literals are conveniently written as decimal fractions, e.g., 9.0 and -3.14159,
or as mantissa-exponent pairs, e.g., 0.0314159E+2, which denotes 3.14159 (that is,
0.0314159 times 102 ) and 3E-6, which denotes .000003 (that is, 3 times 1/106 ).
Java allows the classic arithmetic operations on doubles: addition (whose operator
is +), subtraction (-), multiplication (*), and division (/). Given fractional (double)
operands, the operations calculate doubles as results. For example, we can write (1.2
+ (-2.1 / 8.4)) * 0.33, and the computer calculates
The variable initialization for f starts with the keyword, double, to show that f names
a double.
It is acceptable to mix integers, like c, with doubles in an expression—the result
will be a double, here, 71.6. (This is the case even when the fraction part of the result
is zero: 2.5 * 4 computes to 10.0.)
The preceding examples present an important point about the basic arithmetic
operations, +, -, *, and /:
The “guarantees” mentioned above are used by the Java compiler to track the data
types of the results of expressions without actually calculating the expressions them-
selves. We examine this issue in a moment.
The completed temperature program is simple; see Figure 4. When started, this
application displays,
70
In addition to fractional representation, the Java language lets you write doubles in
exponential notation, where very large and very small doubles are written in terms of
a mantissa and an exponent. For example, 0.0314159E+2 is the exponential-notation
representation of 3.14159. (You see this by multiplying the mantissa, 0.0314159, by
102 , which is 10 raised to the power of the exponent, 2.) Another example is 3e-6,
which denotes .000003 (that is, 3 times 1/106 )—it doesn’t matter whether the E is
upper or lower case.
Mathematicians and engineers who work with doubles often require operations
such as exponentiation, square root, sine, cosine, and so on. The Java designers have
assembled a class, named Math, that contains these methods and many more. For
example, to compute the square root of a double, D, you need only say, Math.sqrt(D):
double num = 2.2;
System.out.println("The square root of " + num + " is " + Math.sqrt(num));
(You do not have to construct a new Math object to use sqrt.) Similarly, Math.pow(D1,
D2) computes D1D2 . Finally, Math.abs(D) computes and returns the absolute value
of D, that is, D without its negation, if there was one. A list of the more useful
computational methods for numbers appears in the supplementary section, “Helper
Methods for Mathematics,” which appears at the end of this chapter.
Although doubles and integers coexist reasonably well, complications arise when
numbers of inappropriate data types are assigned to variables. First, consider this
example:
int i = 1;
double d = i;
What value is saved in d’s cell? When i is referenced in d’s initialization statement,
the integer 1 is the result. But 1 has type int, not double. Therefore, 1, is cast into
3.3. ARITHMETIC WITH FRACTIONS: DOUBLES 71
the fractional number, 1.0 (which has type double), and the latter is saved in d’s cell.
(Computers use different forms of binary codings for integers and doubles, so there
is a true internal translation from 1 to 1.0.)
In contrast, the Java compiler refuses to allow this sequence,
double d = 1.5;
int i = d + 2;
because the compiler determines that the result of d + 2, whatever it might be, will
have data type double. Since a double will likely have a non-zero fractional part,
there would be loss of information in the translation of the double (here, it would be
3.5), into an integer. The Java compiler announces that there is a data-type error in
i’s initialization statement.
If the programmer truly wants to lose the fractional part of 3.5, she can use a cast
expression to indicate this:
double d = 1.5;
int i = (int)(d + 2);
The phrase, (int) forces the double to be truncated into an integer; here, 3 is saved
in i’s cell. We will have occasional use for such casts.
Exercises
2. Recall that one kilometer is 0.62137 of a mile. Rewrite the program, CelsiusToFahrenheit,
into a program, KilometersToMiles, that takes as input an integer value of kilo-
meters and prints as output a double value of the corresponding miles.
3.4 Booleans
The Java language lets you compute expressions that result in values other than
numbers. For example, a calculation whose answer is “true” or “false” is a boolean
answer; such answers arise when one compares two numbers, e.g., 6 > (4.5 + 1) asks
if 6 is greater than the sum of 4.5 and 1; the expression calculates to true, and this
statement,
System.out.println( 6 > (4.5 + 1) );
prints true.
The Java data type, boolean, consists of just the two values, true and false.
Booleans are used as answers to questions; here is an example: We employ the
temperature conversion formula to determine whether a given Celsius temperature is
warmer than a given Fahrenheit temperature. The algorithm is
1. Set the given Celsius and Fahrenheit temperatures.
2. Convert the Celsius amount into Fahrenheit.
3. Compare the converted temperature to the other Fahrenheit temperature.
The corresponding Java statements read this simply:
double C = 22.0;
double F = 77.0;
double C_converted = ((9.0/5.0) * C) + 32;
System.out.print(C + " Celsius warmer than " + F + " Fahrenheit? ");
System.out.println(C_converted > F);
These statements will print
22.0 Celsius warmer than 77.0 Fahrenheit? false
Here are the standard comparison operations on numbers. All these operations
use operands that are integers or doubles and all compute boolean results:
Operation Operator symbol Example
greater-than > 6 > (3 + 1) calculates to
true
less-than < 6.1 < 3 calculates to
false
less-than-or-equals <= (3.14 * -1) <= 0 calcu-
lates to true
greater-than-or-equals >= 1 >= 2 calculates to
false
equality == (1 + 2) == (2 + 1) cal-
culates to true
inequality != 0.1 != 2.1 calculates to
true
3.4. BOOLEANS 73
This declares b and initializes it to false. Indeed, the same action can be done more
directly as
boolean b = false;
because you are allowed to use the literals, false and true as well. You can reassign
a new value to a boolean variable:
b = ((3 * 2) == (5 + 1));
This resets b to true. Notice that arithmetic equality in the above example is written
as ==. Also, arithmetic operations are calculated before comparison operations, which
are calculated before the assignment, so the previous example can be abbreviated to
b = (3 * 2 == 5 + 1);
or just to
b = 3 * 2 == 5 + 1;
Exercises
1. Build a complete Java application that uses the technique in this section to
determine whether a given celsius temperature is warmer than a given fahrenheit
temperature. Test the application with 40 Celsisus and 40 Fahrenheit.
Although it is not recommended to write expressions like the following one, the
precedence rules let us untangle the expression and compute its answer:
3 + -4 * 5 != 6 - 7 - 8
=> 3 + -20 != 6 - 7 - 8
=> -17 != 6 - 7 - 8
=> -17 != -1 - 8
=> -17 != -9
=> true
3.6. STRINGS, CHARACTERS, AND THEIR OPERATIONS 75
It is always best to use ample parentheses to indicate clearly the match of operands
to operators in an expression.
Exercises
1. Calculate the answers for each of these expressions:
(a) 6 * -2 + 3 / 2 - 1
(b) 5.3 + 7 / 2 + 0.1
(c) 3*2%4/5.0*2*3
Literal strings can contain nonletters, such as blanks, numerals, tabs, and backspaces;
here are examples:
– a tab (\t),
– a backspace (\b),
– a single quote (\’),
– a double quote (\"),
– a newline (\n),
76
– a return (\r),
– and a backslash (\\))
The last example shows that some symbols which represent special keys on the key-
board must be typed with a leading backslash. If you print the last string:
System.out.println("!?,.\t\b\’\"\n\r\\");
!?,. ’"
\
because the tab, backspace, quotes, newline, return, and backslash display themselves
correctly in the command window.
As we saw earlier in this chapter, the + operator computes on two strings by
concatenating (appending) them together:
The operator can also force a number (or boolean) to be concatenated to a string,
which is a useful convenience for simply printing information:
• To compare two strings S1 and S2 to see if they hold the same sequence of
characters, write
S1.equals(S2)
3.6. STRINGS, CHARACTERS, AND THEIR OPERATIONS 77
String s = "hello";
System.out.println(s.equals("hel"+"lo"));
System.out.println(s.equals(s));
• To determine a string’s length, use the length() method: for string S, the
message
S.length()
String s = "ab";
int i = s.length();
assigns 2 to i.
• For string, S, S.trim() computes a new string that looks like string S but without
any leading or trailing blanks. For example,
assigns "ab c" to t—both the leading and trailing blanks are removed.
These methods and many others are listed in Table 5. Use the Table for reference;
there is no need to study all the methods at this moment. In addition to the operations
in the Table, you can locate others in the API documentation for class String within
the java.lang package.
Finally, a warning: Do not use the == operation to compare strings for equality! For
reasons that cannot be explained properly until a later chapter, the expression S1 ==
S2 does not validate that strings S1 and S2 contain the same sequence of characters;
instead, it checks whether S1 and S2 are the same identical object. The proper
expression to check equality of two strings is S1.equals(S2).
78
Characters
The individual symbols within a string are called characters, and the data type of
characters is char. A character can be represented by itself by placing single quotes
around it, e.g, ’W’, ’a’, ’2’, ’&’, etc.
We noted earlier symbols like the tab and backspace keys have special codings:
• \b (backspace)
• \t (tab)
• \n (newline)
• \r (return)
• \" (doublequote)
• \’ (single quote)
• \\ (backslash)
Of course, a variable can be created to name a character:
char backspace = ’\b’;
Java uses the Unicode format for characters; inside the computer, characters are
actually integers and you can do arithmetic on them, e.g., ’a’+1 converts (casts) ’a’
into its integer coding, 97, and adds 1.
Begin Footnote: Indeed, it is possible to cast an integer into a printable character:
If one writes (char)(’a’+1), this casts the result of the addition into the character,
’b’—the phrase, (char) is the cast, and it forces the numeric value to become a
character value. End Footnote.
To examine a a specific character within a string, use the charAt(i) method from
Table 5, where i is a nonnegative integer. (Important: The characters within a string,
S, are indexed by the position numbers 0, 1, ..., S.length()-1.) Examples are:
• char c = "abc".charAt(0)
which saves character ’a’ in variable c’s cell.
• String s = "abc" + "de";
char c = s.charAt(2+1);
which saves ’d’ in c’s cell.
• "abc".charAt(3)
which results in an error that stops execution, because there is no character
number 3 in "abc".
80
Also, characters can be compared using the boolean operations in the previous section.
For example, we can compare the leading character in a string, s, to see if it equals
the character ’a’ by writing:
The comparision works correctly because characters are saved in computer storage as
integer Unicode codings, so the equality, inequality, less-than, etc. comparisons can
be performed.
Exercises
1. Calculate the results of these expressions:
(a) System.out.println(t.equals(u));
(b) System.out.println(u.charAt(1) == t.charAt(1));
(c) System.out.println(t.length() - u.length());
(d) System.out.println(u + ’c’);
(e) System.out.println(t.trim());
(f) System.out.println(t.toUpperCase());
boolean b = true;
System.out.println(b * 5);
because the multiplication operator cannot execute with a boolean argument. With-
out doing the actual computation, the Java compiler notices the inconsistency and
announces there is a data-type error.
A data type is a “species” of value, much like horses, lions, and giraffes are species
of animals. Trouble can arise when different species mix, and for this reason, the Java
compiler uses data types to monitor usage of values and variables for compatibility.
More generally, data types are useful because
• They ensure compatibility of operands to operators, e.g., 1 * 2.3 is an accept-
able expression but "abc" * 2.3 is not.
• They predict a property about the result of an expression, e.g., whatever the
result of 1 * 2.3 might be, it is guaranteed to be a double value.
• They help create variable cells of the appropriate format, e.g., double d declares
a cell that is suited to hold only doubles.
Since both -3 and i have data type int, the Java compiler concludes that their
addition will produce an answer that has type int.
The process continues: Since -3 * i has type int, but d has type double, this
implies that the left operand will be automatically cast into a double and that the
answer for (-3 * i) + 2, must be a double. Finally, the less-than comparison will
produce a value that has type boolean.
The process outlined in the previous paragraph is sometimes drawn as a tree, as
in Figure 6. The tree displays the numbers’ data types and how they combine into
the type of the entire expression. A variant of this “data-type tree” is indeed drawn
in primary storage by the Java compiler.
The notion of data type extends to objects, too—the Java compiler uses the name
of the class from which the object was constructed as the object’s data type. For
example, in Figure 4 of Chapter 2 we wrote
GregorianCalendar c = new GregorianCalendar();
82
(( -3 * i )+ d ) > 2
int
double double
double
double double
boolean
which created a cell named c that holds (an address of) an object of data type
GregorianCalendar. The Java compiler makes good use of c’s data type; it uses it to
verify that object c can indeed be sent a getTime message, as in,
System.out.println(c.getTime());
This statement is well formed because getTime is one of the methods listed within
class GregorianCalendar, and c has data type, GregorianCalendar. The Java com-
piler can just as easily notice an error in
c.println("oops");
because c’s data type is of a class that does not possess a println method.
In summary, there are two forms of data types in the Java language:
• primitive types, such as int, double, and boolean
Exercises
Pretend you are the Java compiler.
1. Without calculating the answers to these expressions, predict the data type of
each expression’s answer (or whether there is a data-type error):
3.8. INPUT VIA PROGRAM ARGUMENTS 83
(a) 5.3 + (7 / 2)
(b) 3 * (1 == 2)
(c) 1 < 2 < 3
(d) "a " + 1 + 2
(e) ("a " + 1) * 2
2. Which of these statements contain data type errors?
int x = 3.5;
double d = 2;
String s = d;
d = (d > 0.5);
System.out.println(s * 3);
java CelsiusToFahrenheit 20
• If you use an IDE to execute applications, you type the program argument in the
IDE’s field labelled “Program Arguments,” “Arguments,” or “Command-Line
Parameters.” (See Figure 7.) Then you start the application.
Before we develop the temperature-conversion application, we consider a sim-
pler example that uses a program argument; perhaps we write an application, called
LengthOfName, that accepts a person’s name as its input data and prints the name’s
length in immediate response:
java LengthOfName "Fred Mertz"
The name, Fred Mertz, has length 10.
This is accomplished by this simple application, whose main method uses the name,
args[0], to fetch the value of the program argument:
/** LengthOfName prints the length of its input program argument */
public class LengthOfName
{ public static void main(String[] args)
{ String name = args[0]; // the program argument is held in args[0]
int length = name.length();
System.out.println("The name, " + name + ", has length " + length);
}
}
When an application with program arguments is started, the arguments are automat-
ically placed, one by one, into distinct internal variables named args[0], args[1], and
so on. Using these names, the main method can grab the program arguments.
In general, there can be no program arguments at all or multiple arguments. In
the former case, nothing special needs to be done; in the latter, the arguments are
written next to each other, separated by spaces, e.g.,
java ThisApplicationUsesFourArguments Fred Mertz 40 -3.14159
When the application is started, args[0] holds the string, "Fred", args[1] holds
the string, "Mertz", args[2] holds the string, "40", and args[3] holds the string,
"-3.14159"—all the program arguments become strings, whether they are surrounded
by double quotes or not!
Why are args[2] and args[3] in the example holding strings and not numbers?
The answer lies within the phrase, String[] args, which appears in method main’s
header line: This defines the name, args, for the variables that grab the program
arguments, and the data type, String, which we must use with every main method,
forces every program argument to become a string.
Begin Footnote: In Chapter 5 we learn that args is an example of a parameter,
and in Chapter 8 we learn that args is an array. But we do not need the explanations
of these concepts here to use program arguments. EndFootnote
Therefore, an application that relies on numbers for inputs must construct the
numbers from the strings it reads as the program arguments. We now see how to do
this.
3.8. INPUT VIA PROGRAM ARGUMENTS 85
new Integer(S)
The keyword, new, creates an object from class Integer. The object grabs the
value of the argument (in this case, the value of S is "20"), saves it inside itself,
and calculates its integer translation.
• Send the new object an intValue message. This can be done in two statements,
e.g.,
or in just one:
The message asks the object to return the integer translation. The integer—
here, 20—is returned as the answer and is assigned to c.
In addition to class Integer, there is class Double, which creates objects that
convert strings of numerals and decimals into double values, e.g.,
String approx_pi = "3.14159";
double d = new Double(approx_pi).doubleValue();
3.8. INPUT VIA PROGRAM ARGUMENTS 87
assigns 3.14159 to d.
Begin Footnote: Both class Integer and class Double are located in the Java
package, java.lang. Although it is acceptable to add the statement, import java.lang.*
to the beginning of an application that uses the classes, the Java interpreter will auto-
matically search java.lang when it tries to locate a class; hence, import java.lang.*
is an optional statement and will not be used here. End Footnote
import java.text.*;
new DecimalFormat(PATTERN)
where PATTERN is a string that specifies the significant digits to the left and right
of the decimal point. For example, the pattern, "0.00", in new DecimalFormat("0.00")
states that there must be at least one digit to the left of the decimal point and
there must be exactly two digits to the right of the decimal point. (The right-
most fractional digit is rounded.)
Other patterns, e.g., ".000" or "00.0" are acceptable, and you may consult
the Java API specification for class DecimalFormat for a full description of the
legal patterns.
88
The format method returns the formatted string representation of its double
argument, d; the previous statements place the string, 33.33, in s’s cell.
import java.text.*;
public class Test
{ public static void main(String[] args)
{ ...
DecimalFormat formatter = new DecimalFormat("0.00");
double money = 100.0/3.0;
System.out.println ("$" + formatter.format(money));
...
}
}
java CelsiusToFahrenheit 20
Within the application, the argument is fetched and converted to an integer by writ-
ing:
Recall that we use the name, args[0]—this is the internal variable that grabs the
program argument when the program starts.
Next, we compute the result and format it as a decimal with exactly one fractional
digit:
java CelsiusToFahrenheit 22
args[0] holds the program argument. (We will not depict the System.out object in
these trace steps; just assume it is there.)
The first statement creates a cell named c; initially, there is no integer in it:
CelsiusToFahrenheit
main(args[0] == "20" )
{ int c == ?
> c = new Integer(args[0]).intValue();
double f = ((9.0/5.0)*c) + 32;
...
}
The value placed in the cell is computed by the expression, new Integer(args[0]).intValue().
First, args[0] must be calculated to its value; this is the string, "20":
CelsiusToFahrenheit
main(args[0] == "20" )
{ int c == ?
> c = new Integer("20").intValue();
double f = ((9.0/5.0)*c) + 32;
...
}
The computer invents an internal address for the helper object, say, a1, and inserts
this address into the position where the object was created.
Next, execution of the statements in main pauses while object a1 receives the
message, intValue(), and executes the requested method:
CelsiusToFahrenheit a1 : Integer
main(args[0] == "20" ) holds 20
...
{ int c == ? intValue()
{ > a method that returns 20 }
> c = AWAIT THE RESULT FROM a1;
double f = ((9.0/5.0)*c) + 32;
...
}
3.8. INPUT VIA PROGRAM ARGUMENTS 91
(Note: Object a1 stays the same, and we no longer show it.) Finally, the integer is
assigned to c’s cell:
CelsiusToFahrenheit
main(args[0] == "20" )
{ int c == 20
> double f = ((9.0/5.0)*c) + 32;
System.out.println("For Celsius degrees " + c + ",");
DecimalFormat formatter = new DecimalFormat("0.0");
System.out.println("Degrees Fahrenheit = " + formatter.format(f));
}
The remaining statements in the main method execute in the fashion seen in earlier
execution traces; in particular, a DecimalFormat object is created in the same manner
as the Integer object, and its format method returns the string, "68.0", which is
printed.
Exercises
1. Compile and execute Figure 8 three times with the program arguments 20, 22,
and -10, respectively.
2. Revise the change-making program in Figure 3 so that the dollars and cents
values are supplied as program arguments, e.g.,
java MakeChange 3 46
4. Say that a program, Test, is started with the program arguments 12 345 6
7.89. Write initialization statements that read the arguments and place the
string "12" into a variable, s; place the integer 6 into a variable, i; place the
double 7.89 into a variable, d.
92
• Errors related to spelling, grammar, and context (data types), that the Java
compiler detects. These are called compile-time errors.
• Errors that occur when the program executes, and a statement computes a bad
result that halts execution prematurely. These are called run-time errors or
exceptions.
As we noted in the previous Chapter, the Java compiler performs a thorough job
of checking spelling and grammar. For example, perhaps Line 4 of our program was
written
System.out.println( (1+2(*3 );
Sometimes the narrative (here, ’)’ expected) is not so enlightening, but the identi-
fication of the error’s location is always helpful.
When using variables, take care with their declarations; one common error is
misspelling the data-type name, String:
The error message is not so helpful, but the location of the error, is the tip-off.
With the inclusion of arithmetic expressions, a new form of error, a data-type
error, can appear. For example, the statement,
System.out.println(3 + true);
which states that integers and booleans cannot be added together. Although such
error messages are an annoyance initially, you will find that they are a great assistance
because they locate problems that would certainly cause disaster if the program was
executed.
Data-type errors also arise if a programmer attempts to save a value of incorrect
type in a variable, e.g.,
int i = true;
The compiler will refuse to allow the assignment. The reason should be clear—if the
next statement in the program was
System.out.println(i * 2);
then a disaster would arise. A variable’s data type is a “guarantee” that the variable
holds a value of a specific format, and the Java compiler ensures that the guarantee
is maintained.
A related issue is whether a variable holds any value at all; consider this example:
int i;
System.out.println(i * 2);
The compiler refuses to allow such ambiguities and announces a redeclaration error.
The previous diagnoses by the compiler are a great help, because they prevent
disasters that would arise if the program was executed. Unfortunately, the Java
compiler cannot detect all errors in advance of execution. For example, divison by
zero is a disaster, and it is hidden within this sequence of statements:
94
int i = 0;
System.out.println(1 / i);
These statements are spelled correctly and are correctly typed, but when they are
executed, the program stops with this announcement:
java.lang.ArithmeticException: / by zero
at Test.main(Test.java:4)
This is a run-time error, an exception. The message identifies the line (here, Line 4)
where the error arises and explains, in somewhat technical jargon, what the error is.
The above example of an exception is a bit contrived, but exceptions readily arise
when a program’s user types incorrect input information as a program argument.
Consider this program sequence:
If the program’s user enters 0 as the program argument, the program will halt with
the same exception as just seen—the program is properly written, but its user has
caused the run-time error. Even worse, if the user submits a non-number for the
input, e.g., abc, this exception is generated:
java.lang.NumberFormatException: abc
at java.lang.Integer.parseInt(Integer.java)
at java.lang.Integer.<init>(Integer.java)
at Test.main(Test.java:4)
The error message notes that the exception was triggered at Line 4 and that the
origin of the error lays within the Integer object created in that line.
Finally, some errors will not be identified by either the Java compiler and inter-
preter. For example, perhaps you wish to print whether the values held by variables
x and y are the same, and you write:
int x = 3;
int y = 7;
System.out.println(x = y);
The statements pass the inspection of the Java compiler, and when you start the
application, you expect to see either true or false appear. Instead, you will see 7!
The reason for this surprising behavior is that x = y is an assignment and not a
test for equality (x == y). The statement, System.out.println(x = y), assigns y’s
value to x’s cell and then prints it. For historical reasons, the Java compiler allows
assignments to appear where arithmetic expressions are expected, and results like the
one seen here are the consequence. Remember to use == for equality comparisons.
3.10. JAVA KEYWORDS AND IDENTIFIERS 95
3.11 Summary
Here is a listing of the main topics covered in this chapter:
New Constructions
• variable declaration (from Figure 1):
int quarters = 5;
int c = 22;
double f = ((9.0/5.0) * c) + 32;
96
New Terminology
• operator: a symbol, like * or -, that denotes an arithmetic operation
• data type: a named collection, or “species” of values. In Java, there are two
forms, primitive types, like int, double, and boolean, and reference types, like
GregorianCalendar and DecimalFormat.
• variable: a cell that holds a stored primitive value (like an integer) or an address
of an object.
• initialization: the statement that gives a variable its first, initial value.
• assignment: the statement form that inserts a new value into a variable
Points to Remember
• The Java language lets you calculate on numbers, booleans, strings, and even
objects. These values are organized into collections—data types—so that the
Java compiler can use the types to monitor consistent use of variables and values
in a program.
• class Double. Converts strings of numerals into integers; has method doubleValue(),
which returns the double value of the string argument, S, in new Double(S).
• String. Used like a primitive data type, but it is actually a class; see Table 5
for an extensive list of operations (methods).
2. One inch equals 2.54 centimeters. Use this equivalence to write the following
applications:
(a) One that converts an input of feet to an output of meters; one that converts
meters to feet. (Note: one foot contains 12 inchs; one meter contains 100
centimeters.)
(b) One that converts miles to kilometers; one that converts kilometers to
miles. (Note: one mile contains 5280 feet; one kilometer contains 1000
meters.)
(c) The standard formula for converting kilometers to miles is 1 kilometer =
0.62137 miles. Compare the results from the application you just wrote
to the results you obtain from this formula.
3. Write an application that reads four doubles as its input. The program then
displays as its output the maximum integer of the four, the minimum integer of
the four, and the average of the four doubles. (Hint: the Java method Math.max
(resp., Math.min) will return the maximum (resp., minimum) of two doubles,
e.g., Math.max(3.2, -4.0) computes 3.2 as its answer.)
98
(1 + i)y ∗ p ∗ i
payment =
(1 + i)y − 1
Write an application that computes this formula for the appropriate pro-
gram arguments. (Hint: the power expression, ab , is coded in Java as
Math.pow(a,b).)
(b) Given the above formula, one can calculate the amount of debt remaining
on a loan after n years of payments by means of this formula:
n zn − 1
debtAf terN years = (p ∗ z ) − (payment ∗ )
z−1
where z = 1 + i, and payment is defined in the previous exercise.
Extend the application in the previous exercise so that it also prints the
remaining debt for the Years 0 through 5 of the calculated loan.
3.12. PROGRAMMING PROJECTS 99
7. (a) Starting with a principal, p, the total amount of money accumulated after
contributing c each year for y years, assuming an annual interest rate of i,
goes as follows:
(1 + i)y+1 − (1 + i)
total = (p ∗ (1 + i)y ) + (c ∗ )
i
Write an application that computes total for the four input values.
(b) Given a total amount money saved, total, the annual annuity payment
that can be paid out every year for a total of z years, and still retain a
nonnegative balance, is
total ∗ (1 + i)z−1 ∗ i
payment =
(1 + i)z − 1
assuming i is the annual interest that is paid into the remaining money.
Write an application that computes the formula given the three inputs.
8. The acceleration of an automobile is the rate of change of its velocity. The
formula for computing acceleration is
Vf − V i
acceleration =
t
where Vi is the automobile’s initial velocity, Vf is its finial velocity, and t is
the elapsed time the vehicle took, starting from Vi , to reach Vf . (Note: to use
the formula, the measures of velocity and time must be consistent, that is, if
velocities are expressed in miles per hour, then time must be expressed in hours.
The resulting accleration will be in terms of miles and hours.)
(a) Write an application that computes acceleration based on input arguments
for Vi , Vf , and t.
(b) Write an application that computes the time needed for an automobile to
reach a final velocity from a standing start, given as input value of the
acceleration.
(c) Write an application that computes the final velocity an automobile reaches,
given a standing start and input values of time and acceleration.
9. Say that an automobile is travelling at an acceleration, a. If the automobile’s
initial velocity is Vi , then the total distance that the automobile travels in time
t is
distance = Vi t + (1/2)a(t2 )
(a) The user supplies values for Vi , a, and t, and the application prints the
value of distance.
(b) The user supplies values only for Vi and a, and the application prints the
values of distance for times 0, 1, 2, ...
289932 is
3 days,
8 hours,
32 minutes, and
12 seconds
11. Write an application, DecimalToBinary, that translates numbers into their bi-
nary representations. The program’s input is a program argument in the range
of 0 to 63. For example, when the user types an input of 22, ¡/pre¿ the program
replies:
22 in binary is 010110
(Hint: Study the change-making program for inspiration, and use System.out.print
to print one symbol of the answer at a time.)
12. Number theory tells us that a number is divisible by 9 (that is, has remainder
0) exactly when the sum of its digits is divisible by 9. (For example, 1234567080
is divisible by 9 because the sum of its digits is 36.)
Write an application that accepts an integer in the range -99,999 to 99,999 as
its input and prints as its output the sum of the integer’s digits and whether or
not the integer is divisible by 9. The output might read as follows:
13. In the Math object in java.lang there is a method named random: using Math.random()
produces a (pseudo-)random double that is 0.0 or larger and less than 1.0. Use
this method to write a program that receives as input an integer, n, and prints
as its output a random integer number that falls within the range of 0 to n.
3.12. PROGRAMMING PROJECTS 101
15. Write an application, Powers, that takes as its input an integer, n and prints as
output n’s powers from 0 to 10, that is, n0 , n1 , n2 , ..., n10 .
16. Write an application, PrimeTest, that takes as its input an integer and prints
out which primes less than 10 divide the integer. For example, for the input:
java PrimeTest 28
17. Write an application that computes a worker’s weekly net pay. The inputs are
the worker’s name, hours worked for the week, and hourly payrate. The output
is the worker’s name and pay. The pay is computed as follows:
• gross pay is hours worked multiplied by payrate. A bonus of 50% is paid
for each hour over 40 that was worked.
• a payroll tax of 22% is deducted from the gross pay.
18. Write an application that computes the cost of a telephone call. The inputs
are the time the call was placed (this should be written as a 24-hour time, e.g.,
2149 for a call placed a 9:49p.m.), the duration of the call in minutes, and the
distance in miles of the destination of the call. The output is the cost of the
call. Cost is computed as follows:
• Basic cost is $0.003 per minute per mile.
• Calls placed between 8am and midnight (800 to 2400) are subject to a 20%
surcharge. (That is, basic cost is increased by 0.2.)
• A tax of 6% is added to the cost.
The difficult part of this project is computing the surcharge, if any. To help do
this, use the operation Math.ceil(E), which returns the nearest integer value
that is not less than E. For example, Math.ceil(0.2359 - 0.0759) equals 1.0,
but Math.ceil(0.0630 - 0.0759) equals 0.0.
102
In these optional, supplemental sections, we examine topics that extend the essential
knowledge one needs to program arithmetic.
Longs are needed because answers that fall outside the range of the int type
are unpredictable. For example, write a Java program to compute 2000000000 +
1000000000 and compare the result to 2000000000L + 1000000000L The former over-
flows, because the answer is too large to be an integer, and the result is nonsensical.
If you plan to work with numbers that grow large, it is best to work with longs.
At the opposite end of the spectrum is the byte data type, which consists of
those “small” integers in the range -128 to 127, that is, those integers that can be
represented inside the computer in just one byte—eight bits—of space. For example,
75 appears inside the computer as the byte 01001011. (To make 75 a byte value,
write (byte)75.) Byte variables look like this:
byte x = (byte)75;
One can perform arithmetic on byte values, but overflow is a constant danger.
Java also supports the data type float of fractional numbers with 8 digits of
precision. A float is written as a fractional number followed by the letter, F. For
example, 10.0F/3.0F calculates to 3.3333333. A double number can be truncated to
8 digits of precision by casting it to a float, e.g.,
double d = 10/3.0;
System.out.println( (float)d );
3.13. BEYOND THE BASICS 103
displays 3.3333333, which might look more attractive than 3.3333333333333335, which
would be displayed if the cast was omitted.
Floats can be written in exponential notation e.g.,
float x = 3e-4F;
Data Type
Here is the syntax of Java data types as developed in this chapter:
TYPE ::= PRIMITIVE_TYPE | REFERENCE_TYPE
PRIMITIVE_TYPE ::= boolean | byte | int | long | char | float | double
REFERENCE_TYPE ::= IDENTIFIER | TYPE[]
Types are either primitive types or reference types; objects have data types that
are REFERENCE TYPEs, which are names of classes—identifiers like GregorianCalendar.
Chapter 2 hinted that String[] is also a data type, and this will be confirmed in
Chapter 8.
Semantics: In concept, a data type names a collection of related values, e.g., int
names the collection of integer numbers. In practice, the Java compiler uses data
types to monitor consistent use of variables and values; see the section, “Data Type
Checking” for details.
Declaration
DECLARATION ::= TYPE IDENTIFIER [[ = INITIAL_EXPRESSION ]]? ;
A variable’s declaration consists of a data type, followed by the variable name, fol-
lowed by an optional initialization expression. (Recall that the [[ E ]]? states that
the E-part is optional.) The data type of the INITIAL EXPRESSION must be consistent
with the TYPE in the declaration.
See the section, “Java Keywords and Identifiers,” for the syntax of Java identifiers.
Semantics: A declaration creates a cell, named by the identifier, to hold values
of the indicated data type. The cell receives the value computed from the initial
expression (if any).
The Java compiler enforces that the data type of the expression to the right of the
equality operator is compatible with the type of the variable to the left of the equality.
Semantics: An assignment proceeds in three stages:
1. determine the cell named by the variable on the left-hand side of the equality;
2. calculate the value denoted by the expression on the right-hand side of the
equality;
Expression
The variety of arithmetic expressions encountered in this chapter are summarized as
follows:
EXPRESSION ::= LITERAL
| VARIABLE
| EXPRESSION INFIX_OPERATOR EXPRESSION
| PREFIX_OPERATOR EXPRESSION
| ( EXPRESSION )
| ( TYPE ) EXPRESSION
| STATEMENT_EXPRESSION
LITERAL ::= BOOLEAN_LITERAL
| INTEGER_LITERAL | LONG_LITERAL
| FLOAT_LITERAL | DOUBLE_LITERAL
| CHAR_LITERAL | STRING_LITERAL
INFIX_OPERATOR ::= + | - | * | / | %
| < | > | <= | >= | == | !=
PREFIX_OPERATOR ::= -
INITIAL_EXPRESSION ::= EXPRESSION
4.6 Summary
Realistic programs contain subassemblies that are dedicated to input and output
activities. This chapter presents standard techniques for designing and using input-
output classes. The objectives are to
• employ interactive input, where an application can interact with its user and
request input when needed for computation;
• use inheritance to design graphics windows that display output in the forms of
text, colors, and pictures.
• show how an object can “remember” the “state” of the computation by means
of field variables.
108
The Figure makes several points: First, the application is built from several
classes, where the “start-up class” contains the main method. Since this class controls
what happens next, we call it the controller. The controller asks another component
to read inputs that the user types; the part of the program that is responsible for read-
ing input is called the input-view. The controller uses yet another part for displaying
output; this is the output-view.
The programs in Chapters 2 and 3 had no input-view, and System.out was used
as a simple output-view. In this section, we improve the situation by adding an
input-view that interacts with the user to receive input.
It is best to see this new concept, the input-view, in an example: Say that we revise
the temperature-conversion application so that its user starts it, and the application
4.1. INTERACTIVE INPUT 109
The user interacts with the input dialog by typing an integer, say 20, and pressing
the OK button:
The dialog disappears and the answer appears in the command window:
It is the input-view object that interacts with the user and reads her input. The
extensive library of Java packages includes a class that knows how to construct such
input dialogs; the class is JOptionPane. The crucial steps for using the class to
generate an input-reading dialog are
import javax.swing.*;
so that the Java interpreter is told to search in the javax.swing package for
JOptionPane.
where PROMPT is a string that you want displayed within the dialog. (For the
above example, the prompt would be "Type an integer Celsius temperature:".)
The message, showInputDialog(PROMPT), creates the dialog that accepts the
user’s input. The text string that the user types into the dialog’s field is re-
turned as the result when the user presses the dialog’s OK button. In the above
statement, the result is assigned to variable input. (Like program arguments,
inputs from dialogs are strings.)
Begin Footnote: Perhaps you noted that, although JOptionPane is a class, an
showInputDialog message was sent to it—why did we not create an object from class
JOptionPane? The answer is elaborated in the next Chapter, where we learn that
showInputDialog is a static method, just like main is a static method, and there is
no need to create an object before sending a message to a static method. Again,
Chapter 5 will present the details. End Footnote
Figure 2 shows how the temperature-conversion application is modified to use the
dialog as its input view. As noted above, the input received by the dialog is a string,
so we must convert the string into an integer with a new Integer “helper object”:
int c = new Integer(input).intValue();
The application’s outputs are computed and are displayed in the command window,
but later in this chapter we learn how to display outputs in graphics windows of our
own making.
Of course, an application can ask its user for as many inputs as it desires. For
example, the change-making application in Figure 3, Chapter 3, might be converted
4.1. INTERACTIVE INPUT 111
into an application that asks its user for the dollars input and then the cents input—
the first two statements in Figure 3’s main method are replaced by these, which
construct two dialogs:
String d =
JOptionPane.showInputDialog("Type an integer dollars amount:");
int dollars = new Integer(d).intValue();
String c =
JOptionPane.showInputDialog("Type an integer cents amount:");
int cents = new Integer(c).intValue();
One obvious question remains: What happens when the user presses the dialog’s
Cancel button instead of its OK button? If you try this, you will see that the program
prematurely halts due to an exception:
Exception in thread "main" java.lang.NumberFormatException: null
at CelsiusToFahrenheit2.main(CelsiusToFahrenheit2.java:11)
JOptionFrame’s input dialog is written so that a press of its Cancel button causes it
to return a “no value” string, called null. (This is different from the empty string,
""—null means “no string at all.”)
Although null is assigned to variable input, it is impossible for the new Integer
helper object to convert it into an integer, and the program halts at this point.
Exceptions are studied in more detail later.
Another odd behavior is seen when the application is used correctly and dis-
plays the converted temperature: The application does not appear to terminate, even
though all the statements in its main method have completed. Alas, JOptionPane is
the culprit—it is still executing, unseen.
Begin Footnote: JOptionPane and the other graphics classes in this chapter gener-
ate separate threads of execution, that is, they are separately executing objects. So,
even though the main method’s “thread” terminates, the “thread” associated with
JOptionPane is still executing (perhaps “idling” is a better description), hence, the
application never terminates.
Multiple threads of execution are valuable for programming animations and ap-
plications where multiple computations must proceed in parallel. End Footnote
To terminate the application, use the Stop or Stop Program button on your IDE;
if you use the JDK, press the control and c keys simultaneously in the command
window.
Figure 3 displays the class diagram for the application in Figure 2; JOptionPane has
taken its position as the input view, and the helper classes, Integer and DecimalFormatter,
are included because the controller sends messages to objects created from them.
Exercises
1. Write a sequence of statements that use JOptionPane to read interactively a
person’s name (a string) and the person’s age (an integer).
112
CelsiusToFahrenheit2
JOptionPane main System.out
showInputDialog
Integer DecimalFormatter
intValue format
2. Write an application, class Test, that reads interactively an integer and prints
in the command window the integer’s square. Test this program: What happens
when you, the user, abuse it by typing your name instead of a number? When
you press only the OK key for your input? When you refuse to type anything
and go to lunch instead?
3. Revise the MakeChange program in Figure 3, Chapter 3, so that it uses interactive
input.
as a replacement for the two System.out.println statements at the end of the main
method in Figure 2.
visually pleasing.) Then, setVisible(true) tells sample frame to make itself truly
visible; the result is that the frame in Figure 7 appears on the display.
Alas, the frame in Figure 7 is empty—we have inserted nothing within it to display!
To remedy this, we must construct a panel object and draw text or shapes onto the
panel. The panel is “inserted” into the frame so that we can see it on the display.
1. Starting from class JPanel, we write our own customized variant of a panel
and name it, say, class TestPanel. Within class TestPanel, we include the
instructions for painting Hello to you!, in red letters.
3. We size and show the frame, like we did in Figure 6, and the frame with its
panel appears on the display.
116
import java.awt.*;
import javax.swing.*;
public class TestPanel extends JPanel
{
public void paintComponent(Graphics g)
{ ... // instructions that paint text and shapes on the panels’s surface
}
}
• public class TestPanel asserts that we can construct TestPanel objects from
the new class; we do it by stating, new TestPanel().
• The extra instructions within class TestPanel are held in a newly named
method, paintComponent. Like method main, paintComponent holds instruc-
tions to be executed. Specifically, paintComponent must hold the instructions
that state how to paint text, shapes, and colors on the panel’s surface.
The paintComponent method is started automatically, when the panel appears
on the display, and is restarted automatically every time the panel is iconified
(minimized/closed into an icon) and deiconified (reopened from an icon into a
window) or covered and uncovered on the display.
paintComponent’s header line contains the extra information, Graphics g. Every
panel has its own “graphics pen” object that does the actual painting on the
panel’s surface; g is the name of the pen. (We study this issue more carefully
in the next chapter.)
• Finally, the graphics pen uses classes from an additional package, java.awt, so
the statement, import java.awt.*, must be inserted at the beginning of the
class.
g.setColor(Color.red);
g.drawString("Hello to you!", 30, 80);
The first statement sends a message to the graphics pen, g, asking it to “fill” itself
with red “ink.” (Colors are named Color.red, Color.black, etc. See the section,
“Colors for Graphics,” at the end of the Chapter.) The second message tells g to
write the string, "Hello to you!" on the surface of the window. The numbers, 30
and 80, state the position on the window where string drawing should start, namely
118
30 pixels to the right of the window’s left border and 80 pixels downwards from the
window’s top. (The unit of measurement, “pixel,” will be defined in the next section.)
Finally, the commentary for the paintComponent method contains an extra line of
explanation of the graphic pen:
The reason for the idiosyncratic phrase, @param, is revealed in the next chapter.
Although we might construct a panel object, by stating, new TestPanel, we cannot
execute class TestPanel by itself. A panel must be inserted into a frame object, so
we write a controller class whose main method constructs a panel and a frame, inserts
the panel into the frame, and shows the frame. Figure 10 shows us how.
The first statement within the main method constructs a panel object from class
TestPanel and names it sample panel, and the second statement constructs a frame
object similarly. To insert sample panel into sample frame, we must use the wordy
statement:
sample_frame.getContentPane().add(sample_panel);
Crudely stated, the phrase, getContentPane(), is a message that tells the frame to
“open” its center, its “content pane”; then, add(sample panel) tells the content pane
to hold the sample panel. A precise explanation of these steps will be given in Chapter
10, but until then, read the above statement as a command for inserting a panel into
an empty frame.
4.2. GRAPHICAL OUTPUT 119
FrameTest2 MyOwnFrame
main paintComponent
Once the frame and panel are assembled, we set the size and show the frame as
before.
As usual, class FrameTest2 is placed in a file, FrameTest2.java, and class TestPanel
is placed in the file, TestPanel.java. Both files are kept in the same disk folder (di-
rectory). When we execute FrameTest2, this creates a FrameTest2 object in primary
storage and starts the object’s main method, which itself creates the TestPanel and
JFrame objects, which appear on the display. After these objects are constructed, the
contents of primary storage look like this:
FrameTest2 a1 : TestPanel
paintComponent(Graphics g) { ... }
main(...)
{ ... } The object holds instructions
contained in class JPanel
as well as the variable g == a2
a2 : Graphics a3 : JFrame
setSize(...) { ... }
setColor(...) { ... }
drawString(...) { ... } setVisible(...) { ... }
... ...
[The Graphics object is constructed
automatically with the TestPanel.]
The picture emphasizes that the construction of a TestPanel object causes a Graphics
object (the graphics “pen”) to be constructed also. Also, since TestPanel extends
JPanel, the TestPanel object receives the internal variables and methods of an empty
JPanel object as well as the newly written paintComponent method.
Figure 11 shows the architecture of the simple application. The large arrowhead
from MyOwnFrame to JFrame indicates that the former class extends/inherits the latter.
The Figure provides an opportunity to distinguish between the forms of connec-
tions between classes: The large arrowhead tells us that a TestPanel is a JPanel but
with extra structure. The normal arrowheads tell us that FrameTest2 uses a JFrame
and a TestPanel. The is-a and uses-a relationships are crucial to understanding the
architecture of the application.
120
We finish this section by presenting the panel that constructs the graphics window
in Figure 4; it appears in Figure 12. The controller that uses the panel also appears
in the Figure. Before you study the Figure’s contents too intently, please read the
commentary that immediately follows the Figure.
Class MyPanel in Figure 12 contains several new techniques:
• Within the paintComponent method, variables like frame width, frame heigth,
left edge, width, and height give descriptive names to the important numeric
values used to paint the panel.
• Within paintComponent,
sends a drawRect message to the graphics pen to draw the outline of a rectan-
gle. The drawRect method requires four arguments to position and draw the
rectangle:
Thus, the rectangle’s top left corner is positioned at the values 105 and 70, and
the rectangle is drawn with width 90 pixels and height 60 pixels.
• Similarly,
draws a rectangle, and this time, “fills” its interior with the same color as the
rectangle’s outline. Of course, 0, 0 represents the upper left corner of the panel.
Again,
draws and fills an oval. Since a circle is an oval whose height and width are
equal, the four arguments to the fillOval method are
1. The position of the oval’s left edge, which is computed as left edge +
width (the rectangle’s right border) minus diameter (the circle’s diameter).
2. The position of the oval’s top edge, which is top.
4.2. GRAPHICAL OUTPUT 121
import javax.swing.*;
import java.awt.*;
/** FrameTest3 displays a colorful graphics window */
public class FrameTest3
{ public static void main(String[] args)
{ JFrame my frame = new JFrame();
// insert a new panel into the frame:
my frame.getContentPane().add(new MyPanel());
// set the title bar at the top of the frame:
my frame.setTitle("MyFrameWriter");
// an easy way to color the backgrond of the entire window:
int frame width = 300;
int frame height = 200;
my frame.setSize(frame width, frame height);
my frame.setVisible(true);
System.out.println("Frame has appeared!");
}
}
122
3. The width and height of the oval, which are both just diameter.
This message draws a circle at the position 155 and 70, of width and height 40
by 40 pixels.
To do a decent job of painting, we must learn more about the format of a window
and about the arguments one supplies to methods like drawRect and fillOval; we
study this in the next section.
Within the controller, FrameTest3, we see these new techniques:
• One statement both constructs the panel and inserts it into the frame:
my_frame.getContentPane().add(new MyPanel());
Since the panel is not referenced any more by the controller, there is no need
to attach to it a variable name.
• Two more methods of class JFrame help construct the window: setTitle("MyFrameWriter")
places "MyFrameWriter" into the window’s title bar, and
Exercises
Change MyPanel (and FrameTest3) as follows:
1. The displayed circle is painted black. (Hint: use Color.black.)
5. The rectangle is drawn in the lower right corner of the 300-by-200 window.
(Hint: In this case, the upper left corner of the rectangle would be at pixel
position 210, 140.)
50
100 height
200
width 300
70
105 height
60
width 90
not the bottom, contrary to conventional usage). Figure 13 shows how one locates
the pixel at position (100, 50) within a window sized 300 by 200.
It is traditional to discuss a pixel’s position in terms of its x,y coordinates, where
x is the horizontal distance from the window’s left border, and y is the distance from
the window’s top border.
Using pixels, we can give precise directions to a graphics pen: g.drawRect(105,
70, 90, 60) tells the graphics pen, g, to position the top left corner of a rectangle at
coordinates 105,70; the size of the rectangle will be width 90 by height 60. See Figure
14.
To draw an oval, think of it as a as a round shape that must inscribed within
an imaginary rectangular box. Therefore, g.fillOval(105, 90, 40, 40) tells the
graphics pen to position a 40 by 40 oval (a circle of diameter 40) inside an imaginary
40 by 40 box, at coordinates 105, 90. See Figure 15.
The graphics pen has methods to draw lines, text, rectangles, ovals, and arcs of
ovals. The methods behave like the two examples in the Figures.
When you paint on a panel, ensure that the coordinates you use indeed fall within
the panel’s size. For example, drawing an oval at position, 300, 300 within a window
124
70
105 height
40
width 40
of size 200, 200 will show nothing. Also, assume that a window’s top 20 pixels (of its
y-axis) are occupied by the title bar—do not paint in this region.
Table 16 lists the methods of class Graphics we use for telling a graphics object
what to draw. For each method in the table, we see its name followed by a list of
the arguments it must receive. (For example, drawString(String s, int x, int y)
states that any message requesting use of drawString must supply three arguments—
a string and two integers. For the sake of discussion, the string is named s and the
integers are named x and y.)
It might be best to study the details of Table 16 as they are needed in the examples
that follow.
Every panel must be inserted into a frame, and it is tedious to write over and over
again the instructions that construct the frame and insert the panel into the frame.
It is better to write the panel class so that that the panel itself constructs its own
frame and inserts itself into the frame. This is done using two new and important
techniques: constructor methods and self reference via this.
Here is an example that illustrates the new techniques: Say that you want a
4.3. FORMAT AND METHODS FOR PAINTING 125
Name Description
setColor(Color c) Fills the graphics pen with color, c
drawLine(int x1, int y1, int x2, Draws a line from position x1, y1 to x2,
int y2) y2
drawString(String s, int x, int y) Displays the text, a String, s, starting at
position x, y, which marks the base of the
leftmost letter
drawRect(int x, int y, int width, Draws the outline of a rectangle of size
int height) width by height, positioned so that the
upper left corner is positioned at x, y
fillRect(int x, int y, int width, Like drawRect but fills the rectangle’s in-
int height) terior with the color in the pen
drawOval(int x, int y, int width, Draws the outline of an oval of size width
int height) by height, positioned so that the upper
left corner of the imaginary “box” that en-
closes the oval is positioned at x, y
fillOval(int x, int y, int width, Like drawOval but fills the oval’s interior
int height)
drawArc(int x, int y, int width, Like drawOval but draws only part of
int height, int start angle, int the oval’s outline, namely the part start-
thickness) ing at start angle degrees and extending
counter-clockwise for thickness degrees.
Zero degrees is at “3 o’clock” on the oval;
the arc extends counter-clockwise for a pos-
itive value of thickness and clockwise for
a a negative value.
fillArc(int x, int y, int width, Like drawArc, but fills the interior of the
int height, int start angle, int partial oval, drawing what looks like a
thickness) “slice of pie”
paintImage(Image i, int x, int y, Displays the image file (a jpg or gif im-
ImageObserver ob) age) i with its upper left corner at posi-
tion, x, y, with “image observer” ob. (This
method is employed by examples in Chap-
ter 10; it is included here for completeness.)
126
As usual, we must write a class that extends JPanel whose paintComponent method
has instructions for drawing the clock’s face. But we must also perform the mundane
steps of constructing a frame for the panel, inserting the panel into the frame, and
displaying the frame. Can we merge these steps with the ones that define and paint
the panel? We do so with a constructor method.
Every class is allowed to contain a “start up” method, which automatically ex-
ecutes when an object is constructed from the class. The start-up method is called
a constructor method, or constructor, for short. The constructor method typically
appears as the first method in a class, and it must have the same name as the class’s
name. For example, if we are writing a panel named ClockWriter, then its constructor
method might appear as follows:
/** ClockWriter draws a clock in a panel. */
public class ClockWriter extends JPanel
{ /** ClockWriter is the constructor method: */
public ClockWriter()
{ ... // start-up instructions that execute when the
// object is first constructed
}
new ClockWriter()
• A message is sent to the new object to execute the instructions in its constructor
method, also named ClockWriter.
We wish to write class ClockWriter so that it constructs a panel and paints the
graphical clock, and we will write the class’s constructor method so that it constructs
the panel’s frame, inserts the panel into the frame, and shows the frame. Figure 17
shows how we do this.
When the instruction, new ClockWriter() executes, a ClockWriter object is con-
structed in storage. Then, the constructor method, also named ClockWriter, executes:
1. JFrame clocks frame = new JFrame() constructs a frame object in storage.
3. The last four instructions in the constructor method tell the frame how to
present itself on the display.
Now, whenever you state, new ClockWriter(), the panel displays itself in a frame that
it constructed itself.
The paintComponent method does the hard work of drawing the clock on the panel.
The clock is constructed from a circle and two slender, filled arcs, one arc for the
minutes’ hand and one for the hours’ hand. Based on the current time, in hours and
minutes (e.g., 10:40 is 10 hours and 40 minutes), we use these formulas to calculate
the angles of the clock’s two hands:
minutes_angle = 90 - (minutes * 6)
(The rationale is that a clock face contains 360 degrees, so each minute represents
6 degrees and each hour represents 30 degrees of progress on the face. Subtraction
from 90 is necessary in both formulas because Java places the 0-degrees position at
the 3 o’clock position.)
To fetch the current time, we construct a GregorianCalendar object and uses its
get method to retrieve the current minutes and hours from the computer’s clock.
128
/** The main method assembles the clock in its frame. The method
* is inserted here for testing purposes. */
public static void main(String[] args)
{ new ClockWriter(); }
}
4.3. FORMAT AND METHODS FOR PAINTING 129
Begin Footnote: The get method and its arguments, Calendar.MINUTE and Cale
ndar.HOUR are explained in Table 22; we do not need the details here. End Footnote
Drawing the clock’s hands is nontrivial: The minutes’ hand must be drawn as a
filled arc, slightly smaller than the size of the clock’s face. So that we can see it, the
arc has a thickness of 5 degrees. A smaller arc is drawn for the hours’ hand, and it is
attractive to draw it with a thickness of -8 degrees. (See the explanation of drawArc
in Table 16 for the difference between positive and negative thickness.)
Begin Footnote: Why was the minutes’ hand drawn with a positive (counterclock-
wise) thickness and the hours’ hand drawn with a negative (clockwise) thickness?
Partly, to show that it can be done! But also because the negative thickness makes
the hours’ hand more attractive, because this makes the hand “lean” forwards to-
wards the next hour. See Exercise 2 for a better alternative to the negative thickness.
End Footnote.
Because class ClockWriter contains all the instructions for building and display-
ing the clock, we can write a main method that is just this simple:
java ClockWriter
The Java interpreter finds the main method in class ClockWriter and executes its
instructions.
Begin Footnote: In the next chapter we learn why the main method can be em-
bedded in a class from which we construct objects. End Footnote
You should try this experiment: Start ClockWriter and read the time. Then,
iconify the window (use the window’s iconifiy/minimize button), wait 5 minutes, and
deiconify (“open”) the window—you will see that the clock is repainted with a new
time, the time when the window was reopened!
The explanation of this behavior is crucial: each time a graphics window is
iconified (or covered by another window) and deiconified (uncovered), the window
is sent a message to execute its paintComponent method. For the example in Fig-
ure 17, each time the paintComponent method executes, it constructs a brand new
GregorianCalendar object that fetches the current time, and the current time is
painted.
130
Exercises
1. Modify class ClockWriter so that its hours’ hand is exactly the same length
as the minutes’ hand but twice as wide.
2. Improve the positioning of the hours’ hand in class ClockWriter so that it
moves incrementally, rather than just on the hour. Use this formula:
3. Make the clock “digital” as well as “analog” by printing the correct time, in
digits, at the bottom of the window.
public ClockWriter()
{ ...
clocks_frame.setSize(width, width);
...
}
Field width remembers the panel’s width and is shared by both the constructor
method and paintComponent — it is not “local” or “owned” by either. The field’s
declaration is prefixed by the keyword, private; the keyword means “usable only by
the methods in this class (and not by the general public).”
This simple change makes the class ClockWriter easier to use and maintain,
because a key aspect of the panel’s identity — its width — is listed in a single
variable that is listed in a key position of the class.
Here is a second example that shows we can change the value of a field variable:
Say that we desire a graphics window that “remembers” and prints a count of how
many times it has been painted on the display:
A variable is the proper tool for remembering such a count, but the variable can-
not be declared inside the object’s paintComponent method, because each time the
132
a1 : FieldExample
private int count == 0
paintComponent(Graphics g) { ... count = count + 1; ... }
( also holds the variable g == a2
The diagram shows that the field, count, is indeed a variable cell whose value can be
used by the object’s methods (here, paintComponent). Also, the graphics pen required
by every panel is in fact saved as a field within the panel as well.
4.4. OBJECTS WITH STATE: FIELD VARIABLES 133
/** The main method assembles the panel and frame and shows them. */
public static void main(String[] a)
{ new FieldExample(); }
}
134
Because this example is illustrative and not meant to be part of a serious appli-
cation, the startup method, main, is embedded within class FieldExample. This lets
us quickly test the class by itself, e.g., java FieldExample.
You should compile and execute class FieldExample. By iconifying and deiconi-
fying and by covering and uncovering the window, you will learn when and how the
paintComponent method does its work.
Fields can also hold (the addresses of) objects. For example, we might further
rewrite class ClockWriter in Figure 17 so that the class retains a GregorianCalendar
object in a field:
import java.awt.*;
import javax.swing.*;
import java.util.*;
/** ClockWriter2 draws a clock in a panel. */
public class ClockWriter2 extends JPanel
{ private int width = 200; // the panel’s width
private GregorianCalendar time = new GregorianCalendar(); // the panel’s clock
...
}
The field, time, is constructed when the object itself is constructed, and it is initial-
ized with the address of a newly constructed GregorianCalendar object. When the
paintComponet method draws the clock, it sends messages to (the object named by)
time to fetch the time.
When one class, C1, constructs an object from class C2 and retains it within a
field, we say that C1 has a (or owns a) C2. In the above example, ClockWriter has a
GregorianCalendar. The has-a relationship is sometimes depicted in a class diagram
4.4. OBJECTS WITH STATE: FIELD VARIABLES 135
GregorianCalendar
get
If a field is not initialized, like count in Figure 18, the Java interpreter will
insert an initial value:
• The value in a field’s cell is retained even when the object is at rest (not executing
one of its methods). A field acts like an object’s “memory” or “internal state,”
because its value is remembered and can be referenced the next time a message
is sent to the object to execute a method.
• Field names should never be redeclared in a method. Say that we alter the
constructor method in Figure 18 to redeclare count:
public FieldExample()
{ int count = 0;
int height = 200;
setSize(height * 2, height);
setVisible(true);
}
136
The first statement creates another, distinct, “local” variable that is owned by
the constructor method—the 0 is assigned to this local variable and not to the
field! Unfortunately, the Java compiler allows this dubious double declaration
of count, and the result is typically a faulty program.
Exercises
1. Compile and execute this application:
public class ShowTwoFieldExamples
{ public static void main(String[] args)
{ FieldExample a = new FieldExample();
FieldExample b = new FieldExample();
}
}
How many windows does this construct? (Note: Be careful when you answer—
try moving the window(s) you see on the display.) When you iconify and de-
iconify one of the FieldExample windows, does this affect the appearance of the
other? What conclusion do you draw about the internal states of the respec-
tive FieldExample objects? Draw a picture of the objects in computer storage
created by executing ShowTwoFieldExamples.
2. Finish writing class ClockWriter2 and do an experiment: Start the application,
iconify the window it generates on the display, and after five minutes, reopen
the window. What time do you see? Constrast this behavior to that of class
ClockWriter in Figure 17.
3. Here is a humorous example that illustrates field use. Write a class EggWriter,
that generates a graphics window that displays an egg:
4.4. OBJECTS WITH STATE: FIELD VARIABLES 137
Each time the EggWriter window is iconified and deiconified, the egg is repainted
at half its former size:
Opening and closing the window enough makes the egg shrink into nothingness.
(Hint: Use a field to remember the size of egg to paint; each time the paint
method redraws the egg, it also halves the value of the field.)
and in response to the user’s input, a graphics window is constructed that displays
138
the results:
Because of our limited technical knowledge, it is unclear how we design the the graph-
ics window so that it knows which temperatures to print. This problem is simply
handled in Chapter 5, but for now we improvise: We write an output-view class,
class CelsiusToFahrenheitWriter, whose constructor method contains Steps 1 and
2 of the above algorithm. The Celsuis temperature and its Fahrenheit equivalent are
saved in fields that the paintComponent method uses in Step 3.
The end result is not elegant, but it is effective. Figure 19 shows how we write the
constructor method and the paintComponent method for CelsiusToFahrenheitWriter.
Please read the commentary that follows before studying the Figure’s contents.
The class’s fields are important: celsius and fahrenheit remember the values of
the input and output temperatures, so that the paint method can reference them
when it is time to print them on the window.
As a matter of style, we added two additional fields, which state basic layout
information for the graphics window—if we wish to alter the size or the margins of
the window, we revise these field declarations, which are easily spotted because they
are located at the beginning of the class and their names are spelled with upper case
letters (which is a Java tradition for such “layout” fields).
As promised, the frame’s constructor method has packed into it the steps of read-
ing the input and computing the answer; the input and answer temperatures are saved
4.4. OBJECTS WITH STATE: FIELD VARIABLES 139
in celsius and fahrenheit, respectively. Only then does the constructor method do
its usual business of constructing the graphics window.
The scope of a local variable declaration, starts at the declaration itself and extends
until the first unmatched right brace, }.
For example, consider the local variable, margin, within the paintComponent method
in Figure 18:
public void paintComponent(Graphics g)
{ count = count + 1; // we are painting one more time
g.setColor(Color.black);
int margin = 25;
int line_height = 20;
int first_line = 40;
int baseline = first_line + (line_height * count);
g.drawString("Painted " + count + " times", margin, baseline);
}
Fortunately, the Java compiler refuses to translate this example, so the problem never
arises for an executing program.
On the other hand, it is possible to have two local variables with the same name
if their scopes do not overlap. The above example can be inelegantly repaired by
inserting an artificial pair of set braces:
public static void main(String[] args)
{ { int n = 2;
System.out.println(n);
} // int n’s scope stops here
double n = 4.1;
System.out.println(n); // double n’s value is printed
}
The scope of a field variable declaration extends throughout the class in which the
field is declared, except for those statements where a local variable declaration
with the same name as the field already has scope.
For example, the scope of field count in Figure 18 extends through both methods of
class FieldExample.
Begin Footnote: Unfortunately, the story is a bit more complex than this—the
order in which fields are declared matters, e.g.,
public class ScopeError
{ private int i = j + 1;
private int j = 0;
...
}
generates a complaint from the Java compiler, because the compiler prefers that field
j be declared before field i. End Footnote.
Although it is written in bad programming style, here is a contrived example that
illustrates scopes of fields:
public class Contrived extends JPanel
{ private double d = 3.14;
public Contrived()
{ System.out.println(s);
System.out.println(d);
int d = 2;
System.out.println(d);
s = d + s;
System.out.println(s);
142
setVisible(true);
}
This class uses fields, d and s; when a Contrived object is created and its constructor
method executes, the constructor prints X3.14, then 3.14, then 2, and then 2X3.14.
This shows that the scope of s’s declaration extends throughout the class, whereas
the scope of field d’s declaration extends only as far as the declaration of int d, whose
scope takes precedence at that point.
Later, whenever paintComponent executes, it prints 3.14 2X3.14.
The unpleasantness of the example makes clear that it is best not to use the same
name for both a field variable and a local variable.
1. The tester studies the statements of the program, and invents inputs that might
make the statements malfunction. For example, if a program’s statements com-
pute upon dollars-and-cents amounts, like the change-making program does, the
4.5. TESTING A PROGRAM THAT USES INPUT 143
tester tries to trick the program by submitting zeroes or negative inputs. Or, if
the program contains a statement like int x = y / z, the tester invents inputs
that might cause z to have a value of 0, which would cause a division-by-zero
error. Also, the tester invents enough inputs so that every statement in the
program is executed with at least one input. (This helps check the behavior of
the catch sections of exception handlers, for example.)
This style of testing is sometimes called “glass-box” or “white-box” testing,
because the tester looks inside the program and tries to find weaknesses in the
program’s internal construction.
2. Without looking at the program, the tester pretends to be a typical user and
supplies inputs that mimic those that the program will receive in its actual use.
The inputs will include common mistakes that a user might make (e.g., typing
a fractional number as an input when an integer is required).
This style of testing is sometimes called “black-box” testing, because the internal
structure of the program is not seen, and the tester focusses instead on the
program’s external, visible behaviors.
Exercise
Rewrite the change-making application of Figure 3, Chapter 3, so that it requests two
integer inputs (a dollars value and a cents value) and makes change. Next, define some
“black box” test cases (of input data) for the change-making application in Figure 2,
Chapter 4; define some “white box” test cases. Based on your experiments, write a
144
list of the forms of interactive input will cause the program to operate properly and
write a list of the forms of input that might cause unexpected or incorrect answers.
4.6 Summary
The important aspects of this chapter are summarized in the following subsections.
New Constructions
• class definition by inheritance (from Figure 9):
...
}
public ClockWriter()
{ ...
JFrame clocks_frame = new JFrame();
clocks_frame.getContentPane().add(this);
...
}
New Terminology
• input view: the component(s) of an application that receive input data
• interactive input: input data that the user supplies while a program executes—
the user “interacts” with the executing program.
4.6. SUMMARY 145
• dialog: a window that displays a short message and accepts interactive input, say
by the user typing text or pressing a button. When the user finishes interaction
with the dialog, it disappears.
• null: a special Java value that denotes “no value.” Attempting arithmetic with
null or sending a message to it generates an exception (run-time error).
• graphics pen: the object used by a window’s paint method for painting
• pixel: one of the “dots” on the display screen; used as a measurement unit for
distances within a window.
Points to Remember
• An application can receive interactive input and display simple output by means
of dialogs. In Java, class JOptionPane simply generates dialogs for input and
output.
– a constructor method, which sets the window’s title and size, and
– a paintComponent method, which lists the shapes and colors to be painted
on the panel each time it appears on the display.
• Any object, like a graphics window, can have an internal state. The internal
state is represented as field variables, which are declared separately from the
methods that use them.
and
The application grabs the inputs, calculates its answer and displays it.
2. Write an application that helps a child learn multiplications. The program asks
the child to type two integers and what the child believes is the product of
the integers. Then, the program checks the child’s calculations and prints the
result. The interaction might go as follows:
Type an integer:
(the child types) 13
Type another:
(the child types) 11
Guess the answer:
(the child types) 143
What is 4 times 8?
and the child types her guess, say, 33, and the program replies:
148
What is 4 times 8?
33
The guess was incorrect---the answer is 32.
The child starts the application by stating the upper bound, n, for the integers,
and this ensures that the program generates two integers in the range 0..n.
4. Write an application that uses interactive input to help a home buyer calculate
her expenses for buying and maintaining a house. The program asks the user
to type the following input values:
• The monthly expense of keeping the house. (Note: this amount does not
include the down payment.)
• The annual expense of keeping the house. (Again, exclude the down pay-
ment.)
• The total expense of keeping the house for the number of years needed to
repay the loan. (Include the down payment in this amount.)
The application does not worry about inflation costs in its calculations.
Use this formula to compute the monthly payment on the loan for the house:
payment = {( 1 + i)y ∗ p ∗ i}{(1 + i)y − 1) ∗ 12} where y is the number of years
for repaying the loan, and p is loan’s principal, that is, the price of the house
minus the down payment.
5. Write an application that uses interactive input to help the user calculate her
monthly living expenses. The inputs requested by the program are
• the amount of money the user has budgeted each month for expenses
• the monthly rent
• the weekly groceries bill
• the annual cost of clothing, furniture, and other “dry goods”
• weekly transportation costs (e.g., gasoline, parking fees, bus tickets)
• monthly transportation costs (car payments, car insurance)
4.7. PROGRAMMING PROJECTS 149
Assume that a year has 12.17 months and a month has 30 days, that is, 4.29
weeks.
6. The following projects are meant to give you experience at using methods for
painting. Program a graphics window that displays
try { Thread.sleep(2000); }
catch (InterruptedException e) { }
causes an object to pause for 2 seconds. For any of the graphics windows that
you wrote in response to the previous Project exercise, insert copies of this
“pause” statement in multiple places within the paint method. This will make
the parts of your painting appear one part each 2 seconds.
150
8. Draw a “bar graph” clock that displays the time like this:
(Use Math.sin and Math.cos calculate sine and cosine, respectively. The value
PI is written Math.PI.) Use these equations to
(a) modify class ClockWriter in Figure 17 so that the graphical clock is a
circle plus one line for the minutes’ hand and one line for the hours’ hand.
(b) draw a sun (a circle filled with yellow) that has 6 equally spaced “rays of
sunshine” (yellow lines drawn from the center of the circle spaced every 60
degrees).
(c) improve the display of class ClockWriter so that the numerals 1 through
12 are drawn around the border of the clock face in their correct positions.
(Note: if you find it too tedious to draw all 12 numerals, draw just 2, 4,
and 10.)
4.8. BEYOND THE BASICS 151
4.8.4 Applets
The following optional sections develop details of several of the concepts introduced in
this chapter. The last section explains how to convert an application into an applet,
which is a Java program that can be started within a web browser.
Class JFrame possesses a huge collection of methods, and Table 20 summarizes the
ones we will use for the next five chapters.
All these methods but getGraphics were used in this chapter. As for the latter,
it can be used at any time to “grab” a frame’s graphics pen. For example, a main
method can interfere with the appearance of a frame by grabbing the frame’s pen
and using it:
Constructor
new GregorianCalendar(), creates an object holding the exact time when the
object was created
Methods
getTime() returns the date-and-time held within the object
get(E), where E can be any of the returns the integer value requested by E
arguments listed below
Arguments for get method
Calendar.SECOND requests the seconds value of the time
Calendar.MINUTE requests the minutes value of the time
Calendar.HOUR requests the hours value (12-hour clock) of the time
Calendar.HOUR OF DAY requests the hours value (24-hour clock) of the time
Calendar.DAY OF MONTH requests the day of the month of the time
Calendar.MONTH requests the month of the time (Months are num-
bered 0 to 11.)
Calendar.YEAR requests the current year of the time
Additional methods and arguments can be found in the API for class GregorianCalendar
in the package java.util.
This example shows that getGraphics returns a result that is a value of type Graphics,
which is indeed what is indicated in the Table by the phrase, getGraphics(): Graphics.
The methods in the Table are part of any object created from a subclass of JFrame.
Additional methods for JFrame are found in JFrame’s API in package javax.swing and
are explained in Chapter 10.
For several chapters we have made good use of class GregorianCalendar. A summary
of its most useful methods appears in Table 21.
In Java, colors are objects, and it is possible to send a color a message that asks it
to create a variant of itself with a slightly different tint, e.g., Color.orange.brighter()
and Color.orange.darker(). Try it: g.setColor(Color.orange.darker()). (Of course,
you can still use the Color.orange object, because it is distinct from the Color.orange.darker()
object.)
In addition, you can invent your own colors by stating new Color(r1, g1, b1),
where all of r1, g1, and b1 are numbers between 0 and 255 and are the “red value,”
“green value,” and “blue value,” respectively, of the color. For example, new Color(255,
175, 175) produces pink and can be used just like Color.pink.
4.8.4 Applets
A Java applet is a program whose graphics window appears within a Web page.
Here is some background: You can use a web browser to read files on your com-
puter. Files that contain hyper-text markup language (HTML) commands will be
displayed specially by the browser—you will see multiple columns, multiple type
fonts, pictures, and graphics. In particular, the HTML-command, applet, starts a
Java program (an applet) and displays its graphics window—the browser makes its
own copy of the program and executes it.
To do this, the “controller” that constructs the panel and displays it is the HTML-
file that contains an applet command. Figure 22 shows how the HTML-file might
appear. The statement, <applet code = "MyApplet.class" width=300 height=200>,
marks the position where the web browser should insert the graphics window; the
width and height of the window are indicated so that the browser can reserve the
correct space for the window.
A class that defines a panel ¿ requires several small changes to make it into an
applet:
• It extends JApplet (rather than JFrame).
• It does not need to be inserted into a frame. Its “frame” is the web browser
itself.
• Its constructor method, if any, is renamed into a method named init.
• Its paintComponent method is renamed paint.
4.8. BEYOND THE BASICS 155
5.7 Summary
In this chapter, we write classes that contain methods of our own design. Our
objectives are
• to write public and private methods that accept arguments (parameters) and
reply with results;
• to read and write interface specifications, which describe the behaviors of classes
and their methods.
These techniques will help us write applications whose component structure consists
of classes and methods completely of our own design.
158
5.1 Methods
At the beginning of this text, we remarked that an object owns a collection of meth-
ods for accomplishing work. For example, a program for word processing will have
methods for inserting and removing text, saving files, printing documents, and so
on. A bank accounting program will have methods for depositing and withdrawing
money from a bank account. And, a graphics-window object has methods for setting
the window’s size, setting its title bar, painting the window, and so on.
The term, “method,” comes from real-life examples, such as the plumber (joiner)
who has methods for fixing toilets, stopping leaks, and starting furnaces. A plumber’s
methods might be divided into “public” ones, that is, the methods that customers
ask of the plumber, and “private” ones, that is, shortcuts and tricks-of-the-trade the
plumber uses to complete a customer’s request.
A plumber is a person, an entity, an object. In addition to plumbers, the world
is full of other entities—carpenters, masons, electricians, painters, and so on. Each
of these entities have methods, and the entities work together on major tasks, e.g.,
building a house.
In a similar way, a program consists of multiple objects that communicate and co-
operate by asking one another to execute methods. For example, in the previous chap-
ters, we wrote applications that sent print and println messages to the preexisting
object, System.out. And, we constructed new objects, like new GregorianCalendar(),
from preexisting classes in Java’s libraries and sent messages to the new objects. These
objects cooperated with our applications to solve problems.
We must learn to write our own classes with methods of our own design. So far,
the methods we have written ourselves have been limited to two specific instances:
First, we wrote many applications that contained the public method, main. When
we start an application, an implicit message is sent to execute main. Second, we
learned to write one form of graphics window, which owns a constructor method and
a paintComponent method; the coding pattern looked like this:
import java.awt.*;
import javax.swing.*;
public class MyOwnPanel extends JPanel
{ public MyOwnPanel()
{ ... // instructions for initializing field variables,
// constructing the panel’s frame, and displaying it
}
Recall that the constructor method executes when a new MyOwnPanel() object is con-
5.2. PUBLIC METHODS 159
structed, and the paintComponent method executes each time the panel must be
painted on the display.
In this chapter, we learn to write methods that are entirely our own doing and we
learn how to send messages to the objects that own these methods. In this way, we can
advance our programming skills towards writing programs like the word processors
and accounting programs mentioned at the beginning of this Section. We begin by
learning to write the most widely used form of method, the public method.
_ " _
(_\|/_)
(/|\) ejm97
‘m’
(|) sahr
(Note: the initials next to each image are the image’s author’s.) From time to time,
you might like to display one of the insects in the command window. Each time
you do so, you might write a sequence of System.out.println statements to print
the image, but this is foolish—it is better to write a method that has the ability to
print such an image on demand, and then your applications can send messages to this
method.
For example, here is the method we write to print the first image, a bee:
/** printBee prints a bee */
public void printBee()
{ System.out.println(" ,-.");
System.out.println(" \\_/");
System.out.println(">{|||}-");
System.out.println(" / \\");
System.out.println(" ‘-^ hjw");
System.out.println();
}
the moment, do not worry about the keyword, void, or the brackets, (); they will be
explained later. The method’s body contains the instructions for printing the bee.
A class that holds methods for printing the above images appears in Figure 1. The
Figure shows the above method plus two others grouped into a class, AsciiArtWriter.
There is also a fourth method, a constructor method, which we discuss momentarily.
When we compare class AsciiArtWriter to the graphics-window classes from
Chapter 4, we see a similar format: we see a constructor method followed by public
methods. And, we construct objects from the class in a similar fashion as well:
This statement constructs an object named writer that holds the three public meth-
ods. When the object is constructed, the constructor method is invoked, causing an
empty line to print in the command window.
Think of writer as an object, like System.out. This means we can send messages
to it:
writer.printBee();
Notice the syntax of the invocation: The format is the object’s name, followed by
the method name, followed by the parentheses. The parentheses help the Java com-
piler understand that the statement is indeed a method invocation—this is why the
parentheses are required.
The above format is the usual way of sending a message to an object, and it is
a bit unfortunate that the paintComponent methods we wrote in Chapter 4 were not
invoked in this usual way—paintComponent is a special case, because it is normally
invoked by the computer’s operating system when a window must be repainted on
the display.
Figure 2 shows an application that sends messages to an AsciiArtWriter object
to print two bees and one butterfly.
The advantage of writing class AsciiArtWriter is that we no longer need to re-
member the details of printing the Ascii images—they are saved in the class’s methods.
And, we can reuse the class over and over with as many applications as we like. This
is a primary motivation for writing methods and grouping them into a class. class
AsciiArtWriter is a pleasant addition to our “library” of program components.
Exercises
1. Say that we alter the class in Figure 2 to look like this:
w.printButterfly();
}
}
import javax.swing.*;
public class HelperClass
{ public HelperClass()
{ } // nothing to initialize
/** computeSquareRoot reads an input integer and displays its square root. */
public void computeSquareRoot()
{ String s = JOptionPane.showInputDialog("Type a number:");
double d = new Double(s).doubleValue();
double root = Math.sqrt(d);
164
JOptionPane.showMessageDialog(null,
"The square root of " + d + " is " + root);
}
}
Write an application whose main method invokes the method to help its user
compute two square roots.
4. Write the missing method for this application:
import javax.swing.*;
/** NameLength calculates the length of two names.
* Input: two names, each typed into an input dialog
* Output: dialogs that display the names and their lengths. */
public class NameLength
{ public static void main(String[] args)
{ HelperClass c = new HelperClass();
c.readNameAndDisplayItsLength();
c.readNameAndDisplayItsLength();
JOptionPane.showMessageDialog(null, "Finished!");
}
the object is constructed in computer storage and the class’s constructor method is im-
mediately invoked. For this reason, you should read the phrase, new AsciiArtWriter()
in Figure 2, as both constructing a new object and sending a message to the method
named AsciiArtWriter().
AsciiArtWriter’s constructor does little, but as we saw repeatedly in Chapter
4, a constructor method is often used to “complete” the construction of an object:
The constructor methods for all graphics windows in Chapter 4 contained statements
5.2. PUBLIC METHODS 165
that set the windows’ sizes, background colors, framing, and visibilities. For example,
recall Figure 18, Chapter 4, which constructs a graphics window that displays a count
of the window’s paintings:
import java.awt.*;
import javax.swing.*;
/** FieldExample displays how often a window is painted on the display */
public class FieldExample extends JPanel
{ private int count; // this field variable holds the count of how
// often the window has been painted.
The FieldExample’s constructor initializes count to zero, then frames, sizes, and dis-
plays the panel. Without the constructor to complete the FieldExample object’s
construction, the object would be useless. Review Chapter 4 to see again and again
where constructor methods are used this way.
Because a constructor method is invoked when an object is constructed, it is
usually labelled as a public method. A constructor method must have the same
name as the class that contains it, and again, this is why a statement like new
AsciiArtWriter() should be read as both constructing a new object and invoking
its constructor method. For reasons explained later in this chapter, the keyword,
void, never appears in the header line of a constructor method.
If the constructor has nothing to do, we can write it with an empty body:
public AsciiArtWriter()
{ }
We annotate the execution marker with the numeral, 1, so that we can see the effects
of method invocation.
The first statement creates a storage cell named writer and starts construction
of an AsciiArtWriter object:
DrawArt a1 : AsciiArtWriter
main AsciiArtWriter
{ AsciiArtWriter writer == ? { 2 > System.out.println(); }
1 > writer = AWAIT COMPLETION OF METHOD public void printBee() {...}
...
public void printButterfly() {...}
}
public void printLadybug() {...}
The object is constructed at an address, say a1, and its construction method is in-
voked. A new execution marker, 2>, shows that execution has been paused at position
1> and has started within the invoked method at 2>.
Within the constructor, the println invocation executes next. When this state-
ment completes, the constructor method finishes, the address, a1, is returned as the
result, and execution resumes at point 1>:
DrawArt a1 : AsciiArtWriter
main public void printBee() {...}
{ AsciiArtWriter writer == ? public void printButterfly() {...}
1 > writer = a1; public void printLadybug() {...}
...
}
Once finished, a constructor method always, automatically returns the address of the
object constructed and the method disappears from the object. Notice that a1 is
inserted at the position of the invocation; this lets the assignment complete and the
execution proceed to the next statement:
DrawArt a1 : AsciiArtWriter
main public void printBee() {...}
{ AsciiArtWriter writer == a1 public void printButterfly() {...}
... public void printLadybug() {...}
1 > writer.printBee();
...
}
5.2. PUBLIC METHODS 167
To execute, this invocation, the address of the receiver, writer, must be computed;
it is a1, so the invocation computes to a1.printBee(), meaning that execution starts
within printBee in a1:
DrawArt a1 : AsciiArtWriter
main public void printBee() {...}
{ AsciiArtWriter writer == a1 { 2> System.out.println(...);
... ...
1> AWAIT COMPLETION OF METHOD }
public void printButterfly() {...}
public void printLadybug() {...}
The statements in printBee execute one by one. Once finished, execution restarts at
1>, and printBee “resets” for future use:
DrawArt a1 : AsciiArtWriter
main public void printBee() {...}
{ AsciiArtWriter writer == a1 public void printButterfly() {...}
... public void printLadybug() {...}
1> System.out.println("This is a test.");
...
}
Exercises
1. Here is a class with a constructor and public method:
public Counter()
{ count = 0; }
,-.
\_/
>{|||}-Lucy-
/ \
‘-^ hjw
Then the variable, name, can be used within the body of printBeeWithName and it
causes "Lucy" to be used. Figure 3 shows the modifications.
The main motivation for using parameters is that a method can be invoked many
times with different values of actual parameters and can perform related but distinct
computations at each invocation. For example,
170
writer.printBeeWithName("Fred Mertz");
String s = "Ricardo":
writer.printBeeWithName("Ricky " + s);
In each of these cases, the actual parameter is an expression that computes to a string
and binds to printBee’s formal parameter, name.
For example, the last invocation, writer.printBeeWithName("Ricky " + s), causes
the actual parameter, "Ricky " + s, to compute to "Ricky " + "Ricardo", which
computes to "Ricky Ricardo", which binds to name within method printBeeWithName.
In effect, the invocation causes this variant of printBeeWithName to execute:
Execution Trace
To ensure that we understand the above example, we draw part of its execution trace.
Say that an application is about to draw a bee with a name:
DrawArt a1 : AsciiArtWriter
main public void printBee() {...}
{ AsciiArtWriter writer == a1 public void printBeeWithName(String name){...}
public void printButterfly() {...}
String s == "Ricardo"
public void printLadybug() {...}
1> writer.printBeeWithName("Ricky " + s);
...
}
First, the receiver object is computed; since writer holds address a1, this is the
receiver:
a1.printBeeWithName("Ricky " + s);
Next, the value of the argument is computed. Since s holds the string, "Ricardo",
the argument computes to "Ricky Ricardo":
DrawArt a1 : AsciiArtWriter
main public void printBee() {...}
{ AsciiArtWriter writer == a1 public void printBeeWithName(String name){...}
public void printButterfly() {...}
String s == "Ricardo"
public void printLadybug() {...}
1> writer.printBeeWithName("Ricky Ricardo");
...
}
Only after the argument computes to its result does the binding of actual to formal
parameter take place. This creates an initialization statement in printBeeWithName:
DrawArt a1 : AsciiArtWriter
main ...
{ AsciiArtWriter writer == a1 printBeeWithName
{ 2> String name = "Ricky Ricardo";
String s == "Ricardo"
System.out.println(...);
1> AWAIT COMPLETION OF METHOD ...
... }
...
}
and at this point, printBee’s body behaves like any other method.
172
Exercises
1. What will this application print?
2. Write the missing bodies of this class’s methods; see the application that follows
for help, if necessary.
import javax.swing.*;
/** NameClass remembers a name and prints information about it. */
public class NameClass
{ private String name; // the name that is remembered
This means numbers, booleans, and even (addresses of) objects can be actual param-
eters. Of course an actual parameter must be compatible with the formal parameter
to which it binds. To understand this, we study a series of examples based on this
method for printing an inverse:
which prints 0.333. Because of the declaration of its formal parameter, we cannot
invoke printInverse with doubles, e.g., calculator.printInverse(0.5) will be disal-
lowed by the Java compiler—only integers can be actual parameters to the method.
But if printInverse had been written with this header line:
public void printInverse(double i)
would be acceptable, because the data type of the actual parameter is compatible
with the data type of the formal parameter. Indeed, we could also perform
calculator.printInverse(3);
because integers can be used in any context where doubles are expected. We see this
clearly when we remember that parameter binding is the same as variable initializa-
tion; therefore, the latter invocation generates this binding:
double i = 3;
The particular variable names, if any, that are used in the actual parameters do not
matter—it is the order in which the actual parameters are listed that determines their
bindings to the formal parameters.
For example, the first invocation generates this execution of printInverse:
{ int i = 3;
String pattern = "0.00000";
DecimalFormat formatter = new DecimalFormat(pattern);
double d = 1.0 / i;
String s = formatter.format(d);
System.out.println(s);
}
The order of the actual parameters proves crucial when there is a situation like this
one,
public void printDivision(double n, double d)
{ System.out.println(n / d); }
where the data types of the two formal parameters are identical.
Finally, we modify Figure 4 to show that objects can also be parameters:
Begin Footnote: Indeed, since strings are implemented as objects in Java, we know
already that objects can be parameters, but the example that follows shows that
objects we construct with the new keyword can be parameters also. End Footnote
Remember that every class name generates its own data type, hence there is a data
type, DecimalFormat, and we use it to describe the second formal parameter, which
will bind to an object we construct:
The invocation proceeds like the others: The actual parameters compute to their
results (in this case, the second parameter computes to the address of a DecimalFormat
object) and bind to the corresponding formal parameters.
Now that we are acquainted with the various forms of parameters, we can solve
a couple of mysteries. First, the header line of the paintComponent method one uses
for graphics windows reads
The Graphics g part is a formal parameter, and whenever the operating system sends
a message to paintComponent, the message will contain an actual parameter that is
(the address of) the graphics-pen object that paintComponent uses for painting.
Second, method main’s header line also requires a parameter:
The formal parameter, args, names the collection of program arguments that are
submitted when an application starts. As noted in Chapter 3, the arguments are
extracted from args by the names args[0], args[1], and so on. The data type,
String[], is read “string array”—it describes collections of strings. We study array
data types in Chapter 8.
We finish our examination of parameters with two final observations:
• Constructor methods may use formal parameters in the same fashion as other
public methods.
Exercises
1. Here is a helper class:
public ArithmeticClass(int b)
{ base = b; }
(a) What does this application print? (Draw execution traces if you are un-
certain about parameter passing.)
public class TestArithmeticClass
{ public static void main(String[] args)
{ ArithmeticClass c = new ArithmeticClass(2);
c.printMultiplication("3", 4.5 + 1);
int i = 4;
c.printMultiplication("A", i);
c.printMultiplication("A", i - 1);
}
}
/** printTotal prints the current total of all numbers added so far */
...
}
Write this method, insert it into class ClockWriter in Figure 17, Chapter 4,
and rewrite ClockWriter’s paintComponent method to invoke it.
Next, make paintComponent draw two clocks—one for your time and one for the
current time in Paris.
Note: If you find this exercise too demanding, read the next section and return
to rework the exercise.
5.4. CASE STUDY: GENERAL-PURPOSE OUTPUT FRAME 179
The input is fetched and displayed in our new graphics window, e.g.,
Of course, we can use a JOptionPane-generated dialog for the input-view, but we must
design and write the output-view that has the ability to display a textual string.
Keeping this simple, we design the graphics window so that it displays exactly one
180
line of text and we can state where to position the text. (In the Exercises at the end
of this section, we make the window more versatile.)
We follow these steps when we design a new class:
1. List the class’s methods (its “responsibilities” or “behaviors”), and for each
method, give an informal description that states the method’s behavior and the
arguments the method requires to execute. This includes the constructor method
as well.
2. List the private fields (“attributes”) that will be shared by the the methods.
3. Write the methods’ bodies so that they have the listed behaviors.
All three items above are crucial to the person who writes the class; Item 1 is
important to those who use the class.
Let’s design the output frame; what methods should it have? Perhaps we decide
on two: (i) we can send a message to the frame to print a sentence; (ii)we can
tell the frame where it should print the sentence. Perhaps we can the first method,
writeSentence; obviously, the method will require an argument — the sentence to be
printed. The second method — call it, positionSentence — will require the x- and
y-coordinates that state where the sentence should be printed on the frame.
Also, we will require a constructor method that constructs the frame with some
fixed width and height.
Now, for the attributes: The initial descriptions of the methods suggest that the
frame must have at private fields that remember the current sentence to display and
the x- and y-coordinates of where to print it. Table 5 summarizes what we have
developed so far.
The new class is named MyWriter, and the table is called its interface specification
or specification, for short. The specification indicates the methods we must write and
it also states the ways that others can use the methods we write, so we will retain
the specification as useful documentation after the class is built. You might compare
Table 5 to the ones that summarized the methods for class Graphics (Table 16,
Chapter 4) and JFrame (Table 20, Chapter 4).
The notion of “specification” comes from real-life: For example, when you buy a
tire for your car, you must know the correct size of tire—the size is the tire’s speci-
fication. Your waist and inseam measurement is another example of an specification
that you use when you sew or purchase a new pair of pants. Specifications help you
construct and use objects in real life, and the same is true in computer programming.
Given the specification in Table 5, how do we write its methods? Since MyWriter is
a “customized” graphics window, we can follow the techniques from Chapter 4. This
suggests that we write a class whose paintComponent method paints the sentence on
the window. How will writeSentence ask paintComponent do its work?
The solution is to declare the private field,
5.4. CASE STUDY: GENERAL-PURPOSE OUTPUT FRAME 181
1. assign sentence = s;
Because repaint’s coding lives within class JPanel and because class MyWriter
extends JPanel, it means that every MyWriter object will have its own repaint
182
1. We designed and wrote a class that can be used as a component of many appli-
cations.
3. The users of the class can read the specification to understand how to use the
class’s methods; there is no need to read the class’s coding.
4. The coding used a constructor method and private fields to help the public
methods behave correctly; the public methods invoked each other as needed.
Item 3 of the above list is so crucial that you should always write specifications, even
“after the fact,” for the classes you build. Indeed, there should be a close, if not exact,
match between the specification and the Java comments you attach to the class and
its methods, so the specifications really generate no additional effort.
In the next Chapter, we will appreciate two more benefits of designing and writing
classes like MyWriter:
• When an application is built by several people, it is easier for distinct people
to write, and test the pieces of the application if the application is divided into
classes.
Exercises
1. Explain the behavior of this application that uses class MyWriter:
import javax.swing.*;
public class AnotherExample
{ public static void main(String[] args)
{ MyWriter writer = new MyWriter(300, 200);
String s = JOptionPane.showInputDialog("Please type some text:");
writer.writeSentence(s);
s = JOptionPane.showInputDialog("Try it again:");
writer.writeSentence(s);
writer.repositionSentence(0, 190);
writer.writeSentence(s + s);
}
}
2. Write an application that asks the user to type an integer, computes the square
root of that integer, and uses class MyWriter to display the integer and its
square root, the latter displayed to a precision of 6 decimal places.
5.4. CASE STUDY: GENERAL-PURPOSE OUTPUT FRAME 185
When you add this method, must you revise writeSentence? repositionSentence?
import javax.swing.*;
public class TestTextWriter
{ public static void main(String[] args)
{ TextWriter writer = new TextWriter(300, 200);
String s = JOptionPane.showInputDialog("Please type some text:");
186
writer.print1(s);
s = JOptionPane.showInputDialog("Try it again:");
writer.print1(s);
s = JOptionPane.showInputDialog("Once more:");
writer.print3(s);
s = JOptionPane.showInputDialog("Last time:");
writer.reset1(s);
}
}
in the comments at the head of the method to describe the result computed.
The two statements in the middle of the main method can be compressed into one, if
desired:
188
With a bit of work, we might collect together other conversion formulas for tem-
peratures and save them in class TemperatureConvertor—see the Exercises that fol-
low. As always, by writing functions and collecting them in classes, we can save and
reuse the formulas in many applications. Indeed, in the Java package, java.lang, one
finds class Math, which is exactly such a collection of commonly used mathematical
functions. In addition, the next Chapter shows other crucial uses of functions.
What forms of results can a function return? The answer is: any value that has
a data type can be the result of a function—use the data-type name in the function’s
header line, and use the value in the return statement. Therefore, we have the same
freedom as we have with parameters when we define results of functions—primitive
values as well as objects can be function results.
The Java compiler will let an application “discard” the result of a function, e.g.,
import javax.swing.*;
public class IgnoreConversion
{ public static void main(String[] args)
{ String input =
JOptionPane.showInputDialog("Type an integer Celsius temperature:");
int c = new Integer(input).intValue();
TemperatureConvertor convert = new TemperatureConvertor();
convert.celsiusIntoFahrenheit(c);
System.out.println("The End");
}
}
The sole statement in main invokes the constructor method and ignores the returned
address. Indeed, remember that a constructor method must always return the address
of the object constructed, and for this reason a constructor can never return any other
value as its result—you will never see a return-data-type listed in the header line of
a constructor method.
5.5. RESULTS FROM METHODS: FUNCTIONS 189
Finally, remember that it is good programming policy to make the return state-
ment the final statement of a function. For example, the Java compiler will allow the
following function to compiler and execute:
/** square incorrectly computes a number’s square
* @param num - the number
* @return an incorrect compution of num * num */
public int square(int num)
{ int result = 0;
return result;
result = num * num;
}
Because execution proceeds from the first statement forwards, the badly placed return
statement forces the function to quit prematurely and return the current value of
result, which is 0—the third statement never executes.
Execution Trace
We finish the section with a few steps of the execution trace of the example that
uses the temperature-conversion function in Figure 7. Say that the main method has
reached this point in its execution:
ConvertATemperature
main
{ int c == 18
...
1> TemperatureConvertor convert = new TemperatureConvertor();
double f = convert.celsiusIntoFahrenheit(c);
...
}
As seen before, the initialization constructs the object that holds the function:
ConvertATemperature
main
{ int c == 18
...
1> TemperatureConvertor convert == a1
double f = convert.celsiusIntoFahrenheit(c);
...
}
a1 : TemperatureConvertor
public double celsiusIntoFahrenheit(double c) {...}
...
The invocation binds the value of the actual parameter to the formal parameter, as
usual, and the message to the object at a1 starts execution of the function, which
190
a1 : TemperatureConvertor
public double celsiusIntoFahrenheit(double c) {...}
...
Because function invocation operates the same way as ordinary method invocation,
the execution trace reinforces the intuition that a method whose “result type” is void
“returns” no result at all.
Exercises
1. What does this application print?
public Counter(int i)
{ count = i; }
5.5. RESULTS FROM METHODS: FUNCTIONS 191
(Hint: use algebra to compute the conversion formula.) Next, text the class
with this application:
4. Here is an application that would benefit from a function. Rewrite it with one.
This adjustment does not change the class’s specification in Figure 5—the program-
ming changes are purely internal. But how do we paint a blue border and a white
center? The algorithm takes a bit of thought:
1. Paint the entire window blue.
2. Calculate the size of a rectangle whose size is slightly smaller than the size of
the entire window.
3. Paint, in the center of the blue window, a white rectangle of this slightly smaller
size.
These steps accomplish the desired result. Because the algorithm is self contained, it
makes sense to give it a name, say, makeBorder, and include it as a private method,
which is used by paintComponent. Figure 8 shows the method and the revised class.
194
The private method, makeBorder, is written just like a public method, except
its header line includes the keyword, private. Its parameter, Graphics pen, is the
graphics pen it is given to do painting. The parameter is supplied by method
paintComponent, which invokes makeBorder by merely stating its name—because the
method is a private method, the receiver must be “this” object:
makeBorder(g);
The private method is useful because it maintains the internal structure of the
original class, leaves paintComponent almost exactly the same as before, and keeps
together the “subalgorithm” we wrote for painting the border. If we choose later to
paint the border differently, we need only change the private method.
The construction of makeBorder is driven by this standard motivation for private
methods:
Where a formula or subalgorithm deserves a name of its own, make it into a private
method.
The window’s paintComponent method must draw the three eggs, but our sense of
economy suggests we should write a private method that knows how to draw one
egg and make paint invoke the method three times. Because of our knowledge of
parameters, we specify the method, paintAnEgg, like this:
2. Calculate the egg’s left edge so that the egg is centered in the window, and
calculate the egg’s top edge by measuring from its bottom.
3. Paint the egg so that its upper left corner is positioned at the left edge, top
edge.
4. Return the value of the top edge, in case it is needed later to stack another egg
on top of this one.
Using the parameters, we readily code the algorithm into the private method that
appears in Figure 9. Method paintComponent merely invokes the private method three
times to stack the three eggs. A small main method is included at the bottom of the
class so that the graphics window can be executed directly.
This example illustrates a classic motivation for writing private methods:
Where there is repetition of a task, write one private method whose body does the
task and invoke the method repeated times.
Stated more bluntly, the above slogan warns you, “If you are using the cut-and-paste
buttons on your text editor to copy statements to do the same task multiple times,
then you should be using a private method instead!”
Exercises
1. Write a testing application that constructs these variations of StackedEggsWriter:
You might find it helpful to maximize the window to view some of the results.
2. Add public methods setEggSize1(int size), setEggSize2(int size), and setEggSize3(int
size) to class StackedEggsWriter. Of course, each method resets the size of
the respective egg displayed and repaints the window. Test the modified class
with this application:
5.6. PRIVATE METHODS 197
import javax.swing.*;
public class StackSomeEggs
{ public static void main(String[] args)
{ StackedEggsWriter writer = new StackedEggsWriter(0, 0, 0);
String s = JOptionPane.showInputDialog("Type size of bottom egg (an int):");
writer.setEggSize1(new Integer(s).intValue());
import java.awt.*;
import javax.swing.*;
/** Circles draws three concentric circles */
public class Circles extends JPanel
{ public Circles()
{ JFrame my_frame = new JFrame();
my_frame.getContentPane().add(this);
my_frame.setTitle("TextWriter");
my_frame.setSize(200, 200);
my_frame.setVisible(true);
}
radius = diameter / 2;
g.drawOval(x_pos - radius, y_pos - radius, diameter, diameter);
}
}
5.7 Summary
We now summarize the main points of this chapter:
New Constructions
Here are examples of the new constructions encountered in this chapter:
• public method (from Figure 1):
New Terminology
• method: a named sequence of statements; meant to accomplish a specific task
or responsibility (e.g., writeSentence) A method has a header line (e.g., public
void writeSentence(String s) and a body (e.g., { sentence = s; repaint();
} )
• receiver: the object that receives a message; it executes the method named in
the message.
• actual parameters: arguments that are listed with the method’s name when
invoking the method (e.g., "Lucy" in writer.printBeeWithName("Lucy"))
• formal parameters: variable names that are listed in the method’s header line
(e.g., String name in the header line, public void printBeeWithName(String
name). When the method is invoked, the actual parameters are assigned (or
bound) to the formal parameters (e.g., the invocation, writer.printBeeWithName("Lucy"),
assigns "Lucy" to variable name.)
202
Points to Remember
• A method can be labelled private (for the use of only the other methods within
the class in which it appears, e.g., paintAnEgg) or public (for the use of other
objects, e.g., writeSentence).
• Private methods are written in response to two situations:
1. Where there is repetition of a task, write one private method whose body
does the task and invoke the method repeated times.
2. Where a fundamental concept, formula, or subalgorithm deserves a name
of its own, make it into a private method.
• Public methods are written in response to this situation: A skill or responsibility
that other objects depend upon should be written as a public method.
• Often, we design classes and methods hand in hand, because a class possesses
a collection of related “behaviors,” where the methods encode the behaviors.
• A specification lists the names and describes the responsibilities of a class’s
public methods. Specifications are written for each class because:
1. It becomes easier to use and reuse a class in several different applications.
2. It becomes easier for distinct people to design, write, and test the distinct
classes in an application;
3. It becomes easier to rewrite or replace one class without rewriting all the
others;
• Actual parameters bind to formal parameters in order—the first actual param-
eter binds to the first formal parameter, the second actual parameter binds to
the second formal parameter, and so on.
5.8. PROGRAMMING PROJECTS 203
• Any value that can be assigned to a variable can be an actual parameter that
binds to a formal parameter.
When the child replies, with say, 33, the answer appears in a newly created
graphics window:
2. For practice with private methods, write this application that counts votes for
a small election:
When started, the application asks Candidate 1 to type her name, followed
by her address. The application asks Candidate 2 to do the same. Next, the
204
/A CAR\
60 m.p.h.: =============== -o--o-- 300.4 miles in 5.007 hours
4. Recall that the formula to calculate the distance, d, that an automobile travels
starting from initial velocity, Vsub 0 , and acceleration, a, is defined
d = V0 t + (1/2)a(t2 )
Write an application that asks the user to submit values for V0 and a; the
application produces graphical output showing the distances travelled for times
0, 1, ..., 10.
5. Write a general purpose currency convertor program. The program is started
by giving it three program arguments: the name of the currency the user holds
and wishes to sell, the name of the currency the user wishes to buy, and the
conversion rate from the first currency to the other. For example, the application
might be given these arguments:
USDollar Euro 0.9428
6. Write a class that helps a user display a bar graph that displays up to 6 separate
bars. Here is an example of such a graph: class BarGraphWriter:
(b) Here is table of interesting statistics about the first six planets:
(b) the values of y, for x equals 0, 2, 4, 6, 8, 10. (Note: set y’s scale to the
range 0..100.)
i. y = x2 + 2x + 1
ii. y = 90 − (0.8x)2
iii. y = 20x − (0.5x)3
iv. y = 0.1(x3 ) + x2 − x
(c) Write an application that helps the user plot a graph of her own. The
application asks the user the maximum values for the x- and y- axes, and
then the application asks the user for the values of the six points to be
plotted. The application displays the plotted graph as its answer.
(d) Use class PointGraphWriter to plot the daily high temperatures at your
home for the past six days.
(e) Use class PointGraphWriter to plot the weekly share value over the past
6 weeks of a stock of your choosing
(f) Recall that the formula to calculate the distance, d, that an automobile
travels starting from initial velocity, V0 , and acceleration, a, is defined
d = V0 t + (1/2)a(t2 )
Write an application that asks the user to submit values for V0 and a; the
application produces as its output a plotted graph that shows the distances
travelled for times 0, 2, 4, ..., 10.
The chart was generated from an output-view object with this specification:
5.8. PROGRAMMING PROJECTS 211
(b) these percentages about the income and outlays of the United States gov-
ernment in Fiscal Year 1997:
INCOME:
personal income taxes: 46%
social security and medicare taxes: 34%
corporate income taxes: 11%
excise and customs taxes: 8%
borrowing to cover deficit: 1%
212
OUTLAYS:
social security and medicare: 38%
national defense: 20%
social programs: 18%
interest on national debt: 15%
human and community development: 7%
general government: 2%
(c) the statistics regarding the masses of the planets in the table seen two
exercises earlier.
(d) how you spend your monthly budget
These optional sections build on the core materials in the chapter and show
• how to use the javadoc program to create API documentation for the classes
you write
• how to state precisely the syntax and semantics of method declaration and in-
vocation
• The name of a class should begin with an upper-case letter, and the first letter
of each individual word in the name should be upper case also. Examples are
MyWriter and GregorianCalendar. Underscores, , and dollar signs, $, although
legal, are discouraged.
• The name of a method should begin with a lower-case letter, and the first letter
of each individual word in the name should be upper-case, e.g., writeSentence.
Underscores and dollar signs are discouraged. If the method returns no result
(its return type is void), then use an imperative verb or predicate phrase for
its name, (e.g., printBee or writeSentence). If the method is a function (its
return type is non-void), then use a phrase that describes the result or how to
compute the result (e.g., celsiusIntoFahrenheit).
• When a field variable receives an initial value that never changes, it might be
spelled with all upper-case letters and underscores, e.g., FRAME WIDTH.
Begin Footnote: Java provides a special variant of a field variable, called a final
variable, which is a field whose value is set once and never changed thereafter.
Final variables are studied in Chapter 9. End Footnote
In Chapter 2, we observed that Java’s packages, like java.lang and java.util, are
documented with HTML-coded web pages. The web pages are called the API (Ap-
plication Programming Interface) documentation and were generated automatically
from the Java packages themselves by the tool, javadoc.
The current chapter suggested that every class should have a specification. Fur-
ther, information from the specification should be inserted into the comments that
prefix the class’s methods. Every class’s specification should be documented, ideally
with an an HTML-coded Web page. We obtain the last step for free if the class’s
comments are coded in “javadoc style,” because we can use the javadoc program to
generate the Web pages.
javadoc is a program that reads a Java class, locates all comments bounded by the
format /** ... */, and reformats them into Web pages. For example, we can apply
javadoc to class MyWriter. by typing at the command line javadoc MyWriter.java
(Note: The previous command works if you use the JDK. If you use an IDE, consult
214
the IDE’s user guide for information about javadoc.) Here is the page created for the
class:
The comments from MyWriter have been attractively formatted into a useful descrip-
tion and annotated with additional information about the built-in graphics classes
that were included by inheritance with MyWriter If we scroll the page downwards, we
5.9. BEYOND THE BASICS 215
Now, a user of MyWriter need not read the program to learn its skills—the API
documentation should suffice!
If we wish more information about one of the methods, say, writeSentence, we
216
The commentary from the writeSentence method has been extracted and formatted
in a useful way.
The format of comments that javadoc reads is as follows:
• A class should begin with a comment formatted with /** ... */. The com-
ment must begin with a one-sentence description that summarizes the purpose
5.9. BEYOND THE BASICS 217
of the class. The comment can extend over multiple lines, provided that each
line begins with *. If desired, lines of the form, * @author ... and * @version
... , can be included in this comment to indicate the author of the class and
date or version number of the class.
• Prior to each method in the class, there is a comment stating the purpose of
the method, the purposes of its parameters, and the purpose of the result that
the method returns.
– The comment begins with /**, and each subsequent line begins with *.
The first sentence of the comment must state the purpose of the method.
– Each parameter is documented on one or more lines by itself, beginning
with * @param
– The method’s result is documented on one or more lines by itself, beginning
with * @return
– The comment is terminated with the usual */
– If a method can possibly “throw” an exception (e.g., a division by zero, as
seen in Chapter 3), a comment line labelled @throw can be used to warn
its user.
• If desired, comments of the form, /** ... */, can be included before the fields
and private methods of the class.
Exercise
Apply javadoc to class TemperaturesWriter in Figure 10, once with and once with-
out the -private option. Do the same for class DialogReader in Figure 14.
Occasionally, one writes a method that does not depend on field variables and can
stand by itself, that is, the method need not be owned by any particular object and
need not be coded in any particular class. For historical reasons, such a method is
called static.
The main method, which starts every Java application, is the standard example of
a static method: main need not belong to any particular object or class. In this text,
for each application we typically write a separate “start up” class that holds main.
But main need not be placed in a class by itself—it can be inserted in any one of an
application’s classes, as we saw in Figure 9, where a trivial method was inserted into
class StackedEggsWriter for convenient start-up.
In Java, a static method like main is labelled static in its header line. The label,
static, means that the method can be invoked without explicitly constructing an object
that holds the method. This is indeed what happens when we start a Java application.
Here is a picture in computer storage after the StackedEggsWriter class from
Figure 9 is started:
StackedEggsWriter
main
{ // new StackedEggsWriter() constructed a StackedEggsWriter object
1> }
a1 : StackedEggsWriter
private int FRAME WIDTH == 300
[this is the object
private int FRAME HEIGHT== that is constructed]
200
... // other fields are here
public void paintComponent( ... ) { ... }
private int paintAnEgg( ... ) { ... }
The static portion embedded within class StackedEggsWriter is extracted and kept
separate from the new StackedEggsWriter() objects that are subsequently constructed.
The new StackedEggsWriter() object never contains the static portion of the class.
Technically, the static portion of class StackedEggsWriter is not an “object” like
the objects created by new StackedEggsWriter() (in particular, it does not have an
address), but it does occupy storage and we will continue to draw it to look like an
object.
There can be other static methods besides main. For example, here is a rewrite of
the temperature-conversion application, where main relies on its own private method
to convert the input temperature:
import java.text.*;
import javax.swing.*;
/** CelsiusToFahrenheit2 converts an input Celsius value to Fahrenheit.
* input: the degrees Celsius, an integer read from a dialog
* output: the degrees Fahrenheit, a double */
5.9. BEYOND THE BASICS 219
double d = Math.sqrt(2.0);
Instead of an object name, an invoked static method is prefixed by the name of the
class where the method is coded. This is analogous to starting an application by
typing the name of the class where the main method is coded.
Other examples of static methods are showInputDialog and showMessageDialog
from class JOptionPane. Indeed, as you utilize more of Java’s libraries, you will
encounter classes holding just static methods (or classes with a mixture of static and
normal—nonstatic—methods). Please remember that a static method is invoked by
prefixing it with its class name; a normal method is invoked by constructing an object
from the class and sending the object a message to invoke its method.
220
In this text, the only static method we ever write will be main.
Finally, we note that Java allows fields to be labelled static and public as well!
We will not develop this variation at this time, other than to point out that class
Math uses it to give a convenient name for the mathematical constant, Pi: When you
write,
System.out.println("Pi = " + Math.PI);
you are using a public, static field, named PI, that is declared and initialized within
class Math.
Static fields make later appearances in Chapters 8 and 10.
the compiler verifies that the method’s body uses parameter c the way doubles should
be used. Also, all statements of the form return E must contain expressions, E, whose
data type matches the information in the header line. In the above example, the
compiler does indeed verify that f has type double, which matches the data type that
the header line promises for the function’s result.
When the compiler encounters an invocation, e.g., convert.celsiusIntoFahrenheit(68),
the compiler verifies that
• the data type of convert is a class that has a celsiusIntoFahrenheit method.
• the data type of the actual parameter, 68, is compatible with the data type
of the corresponding formal parameter of celsiusIntoFahrenheit. (More pre-
cisely, the data type of the actual parameter must be a subtype of the formal
parameter’s type; here, an actual parameter that is an integer is acceptable to
a formal parameter that is a double.)
• the answer, if any, returned by the method can be used at the position where
the invocation is located.
But most important, when the Java compiler studies an invocation, the compiler does
not reexamine the body of invoked method; it examines only the method’s header line.
When the compiler examines an entire class, it analyses the class’s fields and meth-
ods, one by one. When it concludes, the compiler collects an interface specification for
5.9. BEYOND THE BASICS 221
the class, based on the information it extracted from the header lines of the class’s
public components. The information is used when the compiler examines another
class that references this first one. In this way, the Java compiler can systematically
analyze an application built from multiple classes.
A more precise description of data-type checking of methods follows in the next
section.
Method Definition
The format of method introduced in this chapter extends the syntax of methods that
appeared at the end of Chapter 2. We now have this form:
METHOD ::= VISIBILITY static? RETURN_TYPE METHOD_HEADER
METHOD_BODY
A method can be public or private and can optionally return a result. The syntax of
TYPE remains the same from Chapter 3.
A method can have a list of parameters, separated by commas. (Recall that * is read
as “zero or more” and the double brackets are used for grouping multiple phrases.)
The syntax of STATEMENT is carried over from Chapter 2, but one form of statement
changes:
INVOCATION ::= [[ RECEIVER . ]]? IDENTIFIER ( ARGUMENT_LIST? )
because the RECEIVER can be omitted when an object invokes one of its own methods.
Also, a new statement form is included:
222
• All of the FORMALPARAMs in a method’s header line are distinctly named identi-
fiers.
• The STATEMENTs in the method’s body are well typed, assuming use of these
extra variables:
In case a field has the same name as a local variable or formal parameter, the
latter takes precedence in usage.
• In the method’s body, the E component of every return E statement has the
data type specified as the method’s RETURN TYPE. (If RETURN TYPE is void, no
return E statements are allowed.)
The semantics of a method definition is that the name, formal parameters, and
body of the method are remembered for later use.
Method Invocation
Method invocations change little from their format in Chapter 2:
In this section, we consider invocations of only normal (non-static) methods; see the
subsection below for comments about invocations of static methods.
Say that the Java compiler must check the data types within an invocation that
looks like this:
As noted in the preceding section, the data-type checking proceeds in several steps:
1. Determine the data type of RECEIVER: if RECEIVER is omitted, then use the class
name where the invocation appears; if RECEIVER is a variable name, then use the
variable’s data type (which must be a class name); if RECEIVER is an arbitrary
expression, calculate its data type (which must be a class name).
Let C0 be the data type that is calculated.
5.9. BEYOND THE BASICS 223
2. Locate the method, NAME0: Starting at class C0, locate the best matching method
with the name, NAME0. (The precise definition of “best matching method” is
given below.) The best matching method will have its own METHOD HEADER, and
say that the header line has this form:
3. Attach the header-line information to the invocation: The Java compiler at-
taches the data-type names, TYPE1, TYPE2, ..., TYPEn, to the actual parame-
ters, EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn, for use when the method
is executed by the Java Virtual Machine.
If the located method, NAME0, is a private method (that is, VISIBILITY is
private) and was found within class Ck, then the label, private Ck, is at-
tached to the invocation as well. If NAME0 is a public method, then the label,
public, is attached.
Therefore, when NAME0 is a public method, the compiler annotates the invocation
to look like this:
4. Return the result data type: The overall data type of the method invocation is
TYPE0. (If TYPE0 is void, then the invocation must appear where a statement is
expected.)
We will see below how the compiler’s annotations are used to locate and execute an
invoked method.
Here is an example. Given this class,
public class Test
{ public static void main(String[] args)
{ FourEggsWriter w = new FourEggsWriter(); // see Figure 2
String s = setTitle(2);
w.setTitle(s);
}
the compiler must analyze the invocation, setTitle(2). Since there is no prefix on
the invocation, the data type of the receiver is this class, Test. A search of class
Test locates the private method, setTitle, whose formal parameter’s type matches
the actual parameter type. This is the best matching method. The compiler annotate
the invocation to read,
setTitle(2: int) private Test;
be the invocation that is analyzed; we assume that only non-static methods are
invoked. Let C0 be the data type of the RECEIVER. (If RECEIVER is omitted, we take C0
to be the name of the class where the invocation appears.) If the invocation appears
within class C0 also, then the best matching method is the method the compiler finds
by searching(all public and private methods of class C0); Otherwise, the best
matching method is the method found by searching(all public methods of class
C0).
The algorithm for searching(some methods of class C0) is defined as follows:
1. Within some methods of class C0, find the method whose header line has the
name and parameters,
such that
The compiler finds the best matching method for the invocation; since the prefix, w,
has type FourEggsWriter, but the invocation appears within class Test, the com-
piler does searching(all public methods of class FourEggsWriter). The compiler
finds no method in FourEggsWriter that matches, but since FourEggsWriter extends
JFrame, it does searching(all public methods of class JFrame). There, the com-
piler locates the method; its header line is
public void setTitle(String title)
The above definition of best matching method will serve us well until we encounter
the difficult issue of method overloading based on parameter type; this is studied in
Chapter 9.
This method must exist for object a, because the Java compiler located it
during the type-checking phase.
• If the SUFFIX is public, then use data type C0 to search for the method:
Starting with the public methods from class C0, search for a method
whose header line has the form,
public ... NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)
If the method is found, select it. Otherwise, search the public methods that
come from class C1, where class C0 extends C1; repeat the search until
the method is found. The method must be found because the compiler
found it during type checking.
4. Bind the parameters: Variables are created with the names TYPE1 NAME1, TYPE2
NAME2, ..., TYPEn NAMEn, and these cells are assigned the values r1, r2, ..., rn,
respectively.
5. Execute the method: The statements in the body of method NAME0 are executed
with the new variables. Any reference to a name, NAMEi, is evaluated as a lookup
into the cell for variable NAMEi. Execution of the body terminates when the end
of the body is reached or when a statement of the form return EXPRESSION is
encountered: the EXPRESSION is evaluated to its result, r, and r is returned as
the result.
Steps 4 and 5 are the same as as executing this code inside object PREFIX:
Static Methods
A static method can be invoked by making the RECEIVER part of an invocation,
RECEIVER NAME0( ... ) be the name of a class, e.g., Math.sqrt(2). Or, if the
RECEIVER part is omitted, and the invocation appears in the body of a static method,
then the invocation is treated as an invocation of a static method.
When the Java compiler encounters an invocation of a static method, it searches
for the best matching method only amongst the static methods. As usual, the search
for the best matching method begins at the class named by the RECEIVER. When the
Java compiler locates the best matching static method, it uses the information in the
method’s header line to annotate the invocation as seen above. The SUFFIX part of
the annotation is static Ck, where Ck is the class where the best matching method
was located.
When the Java Virtual Machine executes the invocation, the suffix, static Ck
tells it to execute the static method in Ck.
4. Remove all constructor methods from the object—they are no longer needed.
6.5.1 Exceptions
6.10 Summary
Now that we have basic skills at designing a program’s component structure, we con-
centrate on writing more complex applications that react more intelligently to their
input data. The applications use control structures to do so:
• We study program control flow and a new statement form, the conditional state-
ment, which enables a method to ask questions about the values of its arguments.
Based on the answers to the questions, the method can control the computation
to a path best suited to the arguments’ values.
• We study logical relational operators and learn the logic behind conditional state-
ments.
This Chapter complements the previous chapter by showing that control and compo-
nent structure are complementary and equally important to program development.
STATEMENT1;
STATEMENT2;
...
STATEMENTn;
this decides the control flow that STATEMENT1 executes first, followed by STATEMENT2
and so on. A method invocation,
RECEIVER.METHOD(ARGUMENTS);
decides the control flow that execution pauses at the place of invocation so that the
statements named by METHOD in RECEIVER execute next.
Sequencing and invocation are control structures because they order or “structure”
a program’s control flow. A programmer must develop intutitions about control flow,
and the execution traces presented in previous chapters were intended to foster these
intuitions.
One deficiency of the two control structures just seen is that they are unable to
change the control flow based on the values of input data or the occurrence of errors.
To remedy this limitation, we must learn a new control structure.
• When driving to the airport, “if the time is after 4 p.m., then avoid heavy traffic
by taking the frontage road to the airport exit; otherwise, take the freeway,
which is more direct.”
• When preparing a Bearnaise sauce, “if the taste requires more emphasis, then
add a pinch of salt.”
The answers to these questions are determined while the algorithm executes. Simi-
larly, a method might ask questions of the actual parameters it receives when it is
invoked. Such questions are posed with a conditional statement, also known as an
if-statement. A conditional statement asks a question, and depending whether the
answer is “yes” (true) or “no” (false), one of two possible statement sequences is
executed.
The syntax of the conditional statement is
232
if ( TEST )
{ STATEMENTS1 }
else { STATEMENTS2 }
where the TEST is an expression that must evaluate to a boolean-typed answer, that
is, true or false. (An example of such an expression is 4 > (2 + 1), which computes
to true; see Chapter 3 for many other examples.) If TEST evaluates to true, then
STATEMENTS1 are executed and statements S2 are ignored. If TEST evaluates to false,
STATEMENTS2 are executed and STATEMENTS1 are ignored.
Here is a method that uses a conditional:
The method cannot forecast the value of the argument sent to it, so it uses a condi-
tional statement that asks a question about the argument. If the method is invoked
with, say, printPolarity(3 - 1), the command window displays
which shows that the test, i < 0, evaluated to false and the statement labelled
by else was executed. The conditional statement is written on several lines to aid
readability; the statements preceding and following the conditional execute as usual.
In contrast, the invocation, printPolarity(-1), causes
to print. This shows that the method chooses its computation based on the value of
its actual parameter—there are two possible control flows through the method, and
the appropriate control flow is selected when the method is invoked.
Some people like to visualize a conditional statement as a “fork in the road” in their
program; they draw a picture of if ( TEST ) { STATEMENTS1 } else { STATEMENTS2
6.2. CONDITIONAL CONTROL STRUCTURE 233
} like this:
TEST?
true false
STATEMENTS1 STATEMENTS2
The two paths fork apart and join together again, displaying the two possible con-
trol flows. The picture also inspires this terminology: We call statements STATEMENTS1
the then-arm and call statements STATEMENTS2 the else-arm. (Many programming lan-
guages write a conditional statement as if TEST then STATEMENTS1 else STATEMENTS2;
the keyword, then, is omitted from the Java conditional but the terminology is pre-
served.)
Occasionally, only one plan of action is desired, and a conditional statement writ-
ten as
if ( TEST )
{ STATEMENTS }
The application uses a conditional statement to ask the question whether the
user typed an appropriate input value. The programmer can safely assume that the
statements in the then-arm execute only when hours has a nonnegative value—the
if-statement’s test provides a precondition that “guards” entry of control flow into
the then-arm.
The then-arm consists of two statements, showing that sequential control can
“nest” within conditional control. Further, one of the statements sends a message,
showing that invocation control can nest within conditional control. It is sensible for
conditional control to nest within conditional control as well, as we see in the next
section.
234
Exercises
1. What will these examples print?
2. Say that variable x is initialized as int x = 1. For each of the following state-
ments, locate the syntax errors that the Java compiler will detect. (If you are
uncertain about the errors, type the statements in a test program and try to
compile them.)
(a) if ( x ) { } else { x = 2; }
(b) if x>0 { x = 2 }
(c) if ( x = 0 ) { x = 2; }; else {}
and the cents should range between 0 and 99. If any of these conditions are violated,
the computation of change making must not proceed.
This situation requires several questions to be asked and it requires that we nest
the questions so that the change is computed only when all the questions are answered
correctly. The algorithm for doing displays a nested structure:
We write the nested structure as several nested conditional statements; see Figure 2.
Each question is expressed in a conditional statement, and the nested is reflected
by the indentation of the statements. Brackets are aligned to help us see the control
structure.
When writing algorithms like the one for change making, some programmers like
to sketch a picture of the questions and the possible control flows. The picture,
called a flow chart, can prove helpful when there are numerous questions to ask and
the dependencies of the questions are initially unclear. Figure 3 shows the flowchart
corresponding to the change-making algorithm. From the flowchart, one can calculate
the possible control flows and consider what input data values would cause execution
of each flow; this is useful for testing the method. (See the Exercises below and the
Section on testing at the end of this Chapter.)
One disadvantage of nested conditionals is difficult readability—when reading a
statement in the middle of nested conditionals, a programmer can quickly forget which
questions led to the statement. For this reason, some programmers prefer to “unnest”
the conditionals by inserting a boolean variable that remembers the answers to the
conditionals’ tests.
We can unnest the conditionals Figure 2 with this technique; see Figure 4. The
boolean, ok, remembers whether the input-checking tests have been answered favor-
ably. If so, calculation of change executes. The conditionals are simplified into if-then
statements (there are no else-arms; recall that if ( TEST ) { S } abbreviates if (
TEST ) { S } else { }). Nesting is greatly reduced, but the formal relationship be-
tween the original algorithm and the one implicit in Figure 4 is not easy to state—by
6.2. CONDITIONAL CONTROL STRUCTURE 237
read dollars;
dollars negative?
considering all control flows we can determine whether the two applications behave
exactly the same way.
Exercises
1. What will this example print?
int i = 1;
if ( i < 0 )
{ System.out.println("a"); }
else { System.out.println("b");
if ( i == 1 )
{ System.out.println("c"); }
System.out.println("d");
}
System.out.println("e");
2. Use Figure 3 to devise test cases such that each control flow through MakeChangeAgain
is executed by at least one test case. Test the application with your test cases.
4. The applications in Figures 2 and 4 do not behave exactly the same. Find a test
case that demonstrates this, and explain what changes must be made to Figure
4 so that it behaves exactly the same as Figure 2 with all possible inputs.
if ( E1 )
if ( E2 )
S1
else S2
After some thought, we can correctly conclude that the then-arm that mates to the
test, if ( E1 ), is indeed the second conditional statement, if ( E2 ) S1, but an
unresolved question is: Does else S2 belong to if ( E1 ) or if ( E2 )? The Java
compiler adopts the traditional answer of associating the else-arm with the most
recently occurring test, that is, else S2 mates with if ( E2 ). You should not rely
on this style as a matter of course; the price of inserting brackets is small for the
clarity gained.
Another undetected error occurs when an extra semicolon is inserted, say, just
after the test expression. This statement from Figure 7 is altered to have an extra
semicolon:
if ( minute < 10 );
{ answer = answer + "0"; }
Because of the semicolon, the compiler treats the conditional as if it has no then- or
else- arms! That is, the compiler reads the above as follows:
• i >= 3 (i is less-than-or-equals 3)
This statement’s test calculates to true if the first phrase, cents < 0, computes to
true. If it computes to false, then the second phrase is computed—if cents > 99
computes to true then the test computes to true. If both phrases compute to false,
then so does the test. In this way, two related questions can be efficiently asked at
the same point in the program.
In a similar manner, we might use logical “and” (conjunction) to write an expres-
sion that asks whether integer variable minutes falls in the range 0..59:
(minutes >= 0) && (minutes <= 59)
The symbol, &&, denotes “and.” A small execution trace of this expression would be
helpful; assume that minutes holds 64:
(minutes >= 0) && (minutes <= 59)
=> (64 >= 0) && (minutes <= 59)
=> true && (minutes <= 59)
=> true && (64 <= 59)
=> true && false => false
Since one of the arguments to && resulted in false, the test’s result is false.
Table 5 lists the commonly used relational operations and their calculation rules.
The semantics in the Table should be followed exactly as written; consider this ex-
ample:
(2 != 1) || (x == 3.14)
=> true || (x == 3.14)
=> true
Since the first argument to the disjunction, ||, resulted in true, there is no need to
compute the result of the second argument, so the answer for the expression is true.
We may write expressions with multiple relational operations, e.g.,
(x == 2) || (x == 3) || (x == 5) || (x == 7)
6.3. RELATIONAL OPERATIONS 243
Operator Semantics
E1 && E2 conjunction (“and”):
E1 || E2 disjunction (“or”):
!E negation(“not”):
returns true exactly when cents’s value falls outside the range 0..99. For example,
say that cents is 12:
!(cents >= 0 && cents <= 99)
=> !(12 >= 0 && cents <= 99)
=> !(true && cents <= 99)
=> !(true && 12 <= 99)
=> !(true && true) => !true => false
Exercises
1. Calculate the answers for each of the following expressions. Assume that int x
= 2 and double y = 3.5.
6.4. USES OF CONDITIONALS 245
what results are returned by minus(3, 2)? minus(2, 3)? minus(-4, -5)?
minus(4, -5)?
3. For each of these methods, write a body that contains only one conditional
statement. (The conditional’s test uses relational operations.)
(a) /** isSmallPrime says whether is argument is a prime number less than 10
* @param i - the argument
* @return true, if the argument is as prime less than 10;
* return false, otherwise. */
public boolean isSmallPrime(int i)
(b) /** divide does division on its two arguments
* @param x - a nonnegative value
* @param y - a value not equal to zero
* @return (x / y), if the above conditions on x and y hold true;
* return 0, otherwise. */
public double divide(double x, double y)
• to equip a method to defend itself against nonsensical input data and actual
parameters
• to help a method take a plan of action based on the values of the data and
parameters.
246
1. If either of the values of the hour and minutes are nonsensical, then set the
answer to an error message. Otherwise, perform the remaining steps:
(a) Calculate the correct hour, taking into account that hours of value 13..23
are reduced by 12 and that the 0 hour is written as 12; append this to the
answer.
(b) Calculate the correct minute, remembering that a minute value of 0..9
should be written with a leading 0 (e.g., 2 is written 02); append this to
the answer.
(c) Calculate whether the time is a morning (a.m.) time or an afternoon (p.m.)
time; append this to the answer.
Figure 7 shows the method based on this algorithm. The first conditional examines
the parameters for possible nonsensical values. (As stated in Table 1, || denotes “or.”)
If the test computes to true, an error message is constructed.
Otherwise, the the function assembles the twelve-hour time, symbol by symbol,
within the string variable, answer, as described by the algorithm we just studied. It is
essential that the brackets for each conditional’s arms be placed correctly, otherwise
the method will behave improperly—it helps to align the matching brackets and
indent the arms, as displayed in the Figure.
Figure 7 is fascinating because the nested conditional structures are essential to
computing the correct result. Study the Figure until you understand every detail.
We finish with a second use of conditionals—responding to input dialogs. Recall
that a Java input dialog presents the user with a text field for typing input, and two
6.4. USES OF CONDITIONALS 247
buttons:
When the user presses OK, the text entered into the text field is returned to the
application that constructed the dialog. But if the user presses Cancel, the text is
lost, and a special value, called null, is returned instead.
With the help of a conditional statement, we can check whether a user has pressed
OK or Cancel on the dialog:
248
Here is an example that shows this technique: We might insert the twelveHourClock
method into a class TimeConvertor and use it with a main method that uses input
dialogs. The algorithm for main goes like this:
1. Construct an input dialog that asks the user for the hours, an integer between
0 and 23.
3. Construct another dialog that asks for the minutes, an integer between 0 and
59.
4. If the user pressed Cancel, then use 0 for the minutes amount and continue.
The example algorithm means to show us how we can take different actions based on
a press of the Cancel button. Here is how the method checks the button presses:
Exercises
1. Test twelveHourClock with each of these times: 9,45; 23,59; 0,01; 50,50; -12,-12;
24,0.
2. Use twelveHourClock in an application that obtains the current time (using a
GregorianCalendar object), converts it into a string (using twelveHourClock),
and displays the string in the command window.
3. Write a method that meets this specification:
Use the method in an application that asks the user to type a numerical score
and prints the corresponding letter grade.
4. Improve the MakeChangeAgain application so that
(a) answers of zero coins are not displayed. For example, the change for 0
dollars and 7 cents should display only
nickels = 1
pennies = 2
(b) if only one of a kind of coin is needed to make change, then a singular (and
not plural) noun is used for the label, e.g., for 0 dollars and 46 cents, the
application prints
1 quarter
2 dimes
1 penny
behavior. But there are rare cases, when a fatal error has occurred in the middle of a
computation, where terminating the flow of control is tolerable. We briefly consider
techniques for premature termination due to an error.
6.5.1 Exceptions
Even though it might be well written, a program can receive bad input data, for
example, a sequence of letters might be received as input when a number was required:
int i = new Integer(args[0]).intValue();
If the program’s user enters abc as the program argument, the program will halt with
this fatal error, and we say that an exception is thrown:
java.lang.NumberFormatException: abc
at java.lang.Integer.parseInt(Integer.java)
at java.lang.Integer.<init>(Integer.java)
at Test.main(Test.java:4)
The message notes that the exception was triggered at Line 4 and that the origin of
the error lays within the Integer object created in that line.
Throwing an exception alters the usual control flow—the program stops execution
at the program line where the exception arises, and control is “thrown” out of the
program, generating the error message and terminating the program.
Exceptions are thrown when potentially fatal errors arise, making it too dangerous
to continue with the expected flow of control. For example, integer i in the above
example has no value if "abc" is the input, and it is foolish to proceed with execution
in such a case. Here is another situation where a fatal error might arise:
/** convertHoursIntoSeconds converts an hours amount into seconds
* @param hours - a nonnegative int
* @return the quantity of seconds in hours */
public int convertHoursIntoSeconds(int hours)
{ int seconds = 0;
if ( hours < 0 )
{ JOptionPane.showMessageDialog(null,
"ConvertHours error: negative input " + hours);
// should control proceed?
}
else { seconds = hours * 60 * 60; }
return seconds;
}
because the client object that invoked the method will proceed with the wrong answer
and almost certainly generate more errors.
In such a situation, premature termination might be acceptable. A programmer
can force an exception to be thrown by inserting a statement of this form:
where ERROR MESSAGE is a string. The statement terminates the control flow and
throws an exception, which prints on the display with the line number where the
exception was thrown and the ERROR MESSAGE. Figure 8 shows the method revised to
throw an exception when the actual parameter is erroneous.
This technique should be used sparingly, as a last resort, because its usual effect
is to terminate the application immediately.
It is possible for an application to prevent termination due to an exception by
using a control structure called an exception handler. Exception handlers are easily
overused, so we delay their study until Chapter 11.
System.exit(0);
This statement does not generate an error message on the display, and like throwing
exceptions, should be used sparingly.
252
Java allows the return statement to be used without a returned value when a method’s
header line’s return type is void.
Second, here is a method that immediately returns a useless answer when an error
arises:
This example shows that a method may possess multiple return statements.
Premature returns can be easily abused as shortcuts for reducing nested condi-
tionals, and you are encouraged to use relational operators for this purpose instead.
if ( i == 2 )
{ System.out.println("2"); }
else { if ( i == 5 )
{ System.out.println("5");
j = j + i;
}
else { if ( i == 7 )
{ System.out.println("7");
6.6. THE SWITCH STATEMENT 253
j = j - i;
}
else { System.out.println("none"); }
}
}
The nesting is annoying, because the nested conditional merely considers three pos-
sible values of i.
Java provides a terser alternative to the nested if-else statements, called a switch
statement. Here is the above example, rewritten with the switch:
switch ( i )
{ case 2: { System.out.println("2");
break;
}
case 5: { System.out.println("5");
j = j + i;
break;
}
case 7: { System.out.println("7");
j = j - i;
break;
}
default: { System.out.println("none"); }
}
The above switch statement behaves just like the nested conditional.
The syntax we use for the switch statement is
switch ( EXPRESSION )
{ case VALUE1 : { STATEMENTS1
break;
}
case VALUE2 : { STATEMENTS2
break;
}
...
must be integer or character literals (e.g., 2 or ’a’), and no two cases can be labelled
by the same constant value.
The semantics of the above variant of switch goes as follows:
switch ( letter )
{ case ’C’: { ...
break;
}
case ’F’: { ...
break;
}
case ’K’: { ...
break;
}
case ’R’: { ...
break;
}
default: { System.out.println("TempConverter error: bad code"); }
}
switch ( i )
{ case 2: { System.out.println("2");
}
case 5: { System.out.println("5");
j = j + i;
break;
}
default: { System.out.println("no"); }
}
6.7. MODEL AND CONTROLLER COMPONENTS 255
CONTROLLER
INPUT VIEW
OUTPUT VIEW MODEL
if ( i == 2 )
{ System.out.println("2"); }
else if ( i == 5 )
{ System.out.println("5");
j = j + i;
}
else if ( i == 7 )
{ System.out.println("7");
j = j - i;
}
else { System.out.println("none"); }
The Java compiler will read the above nested conditional the same way as the one at
the beginning of the Section.
controller next awakens the output-view and tells it to display the answers. Some-
times the model and output-view communicate directly—this is why the Figure also
contains an arc between output-view and model. Also, it is common for the input and
output views to be combined into one component, which we do ourselves in Chapter
10.)
We call the structure in Figure 3 a model-view-controller architecture.
(Footnote: The architecture in Figure 3 is a simplication of the model-view-
controller architecture used in Smalltalk applications; the differences are discussed
in Chapter 10. End Footnote.)
Why should we bother to build a program in so many pieces? There are three
main answers:
1. The classes that generate the objects can be reused—one example is class
MyWriter from Figure 6, Chapter 5, which is an output view that can be reused
by many applications.
2. The pieces organize an application into manageably sized parts with specific
duties. It is a disaster if we build a complex application within one or two classes,
because the classes become too complicated to understand. If we organize a
program into its input-output parts, a model part, and a controller part, then
we have a standardized architecture whose parts can be written and tested
one at a time. Also, the complete program can be seen as an assembly of
standardized parts into a standard architecture.
• writing a new class to generate the controller that connects together the parts
of the application.
This approach requires that one maintain a “library” of classes for application build-
ing. The Java packages, java.lang, java.util, javax.swing, etc., are meant to be a
starter library—and one that you should start studying—but you should start col-
lecting your own classes as well. And the notion mentioned above of “extending” an
6.7. MODEL AND CONTROLLER COMPONENTS 257
Each activity is called a use-case behavior, or use case, for short. The collection
of use cases helps us design the program’s user interface (the view) so that all
the desired buttons, text fields, dialogs, and displays are present. The use cases
also help us design the program’s model so that the model’s class has methods
to do all the desired computations.
5. Integrate the classes into the architecture and test the system:
Finally, we assemble the classes into a complete program and test the program
to see if it performs the actions specified in the original set of use-case behaviors.
After some discussions with the potential users of the bank-account manager, perhaps
we determine that the program’s input is a sequence of commands, each of which must
be one of the following forms:
Here is a sample use case: a user submits a deposit of $50.50 into an input dialog
as follows:
(The d denotes “deposit,” of course.) The program responds to the deposit command
by displaying the transaction and the current balance in the output view. The deposit
260
A second use case might illustrate a withdrawal of $30.33. Again, a command, say,
D 30.33, would be typed into an input dialog, and the program’s output view would
show this:
Other use cases, illustrating both proper and improper behavior, should be considered:
• entering a quit command;
• attempting to withdraw more money that the account holds in its balance;
• attempting to deposit or withdraw a negative amount of money;
and so on. More and more use cases give us more insight into how the write the
application.
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 261
• Because methods deposit and withdraw return results that are booleans, we list
the data type of the result after the name of the method. (This is the standard
convention, but when we code the method in Java, we place the data type of
the result before the name of the method.)
The model’s attribute, balance, remembers the account’s balance. The attribute is an
int variable, holding the balance, expressed in cents, in the account (e.g., 10 dollars
is saved as 1000 cents); this eliminate troubles with fractions that arise when one
maintains money with a double value.
Since balance is a private variable, it cannot be used directly by the client objects
that use BankAccounts. Indeed, methods deposit and withdraw must be used to
alter balance’s value. Methods that alter the values of attributes are called mutator
methods. Again, the specifications of deposit and withdraw are suffixed by a colon
and boolean, which asserts that the methods return a boolean-typed result.
...
264
of method invocations to the object. The method invocations should mimick to some
degree the possible use cases that motivated the application’s design.
One simple, direct way to test the model is to write a main method that includes
a sequence of method invocations. Here is one sample:
// try a deposit:
boolean test1 = tester.deposit(5050); // we deposit 5050 cents
System.out.println("Deposit is " + test1 + ": " + tester.getBalance());
// try a withdrawal:
boolean test2 = tester.withdraw(3033);
System.out.println("Withdrawal is " + test2 + ": " + tester.getBalance());
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 265
// try a mistake:
boolean test3 = tester.withdraw(6000);
System.out.println("Withdrawal is " + test3 + ": " + tester.getBalance());
The main method can be inserted into class BankAccount and started from that class,
or it can be placed into a small controller class of its own.
As noted earlier, your IDE probably lets you construct the BankAccount object
directly by selecting a menu item, and you can likely test the new object’s methods
by selecting another IDE menu item and entering the method invocations one at a
time, e.g.,
tester.getBalance();
and then
test.deposit(5050);
and so on.
D 50.50
into their parts: a text string, "D", and an integer, 5050. Because BankWriter’s
constructor method takes no arguments, we omit it from the table.
Within BankWriter are two methods, both named showTransaction—we say that
the method name is overloaded. Notice that the header lines of the two same-named
methods have different forms of formal parameters: The first of the two methods
is invoked when a showTransaction message with a string parameter and an inte-
ger parameter is sent to the BankWriter object; the second method is used when a
showTransaction message is sent with just a string parameter.
AccountManager AccountController
main(...) processTransactions() BankAccount
private int balance
deposit(int amount): boolean
withdraw(int amount): boolean
getBalance(): int
BankReader
readCommand(): char BankWriter
readAmount(): int showTransaction(String message,
int amount)
showTransaction(String message)
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 267
a class diagram, because it diagrams the classes and how they are connected.
The model and view classes have already been developed; the AccountController
uses a processTransactions method to control the flow of execution of the meth-
ods in the view and model components. Finally, we use a separate start-up class,
AccountManager, to construct and connect the application’s components.
Figure 13 displays a rich collaboration between the application’s components.
A collaboration between components is sometimes called a coupling; for example,
the BankWriter is “coupled” to the BankAccount, which means the former cannot
operate without the latter. Also, changes to BankAccount might affect the operation
of BankWriter.
It is best not to have one component to which many others are coupled—if such a
component’s methods are changed, then many other components must be changed as
well. In the Figure, we see that the model, BankAccount, is the most sensitive compo-
nent in this regard. Also, since the controller is coupled to the most components, it
is the most likely to be rewritten if there are changes to any part of the application.
An ideal software architecture will minimize the coupling between components so
that changes in one component do not trigger revisions of many other components.
This in effect removes the leading and trailing blanks from input, leaving the
variable with the value "some data". The trim method is useful because users
often carelessly add leading and trailing spaces to the input text they type, and
the spaces sometimes interfere when checking for equality of strings, e.g,
if ( input.equals("some data") )
{ ... }
• A second issue with text input is the case in which letters are typed. For
example, if a user is asked to type F or C as an input letter, the user is likely to
268
type a lower case f or c instead. For this reason, the toUpperCase method can
be used to convert all input letters to upper case, e.g.,
input = input.toUpperCase();
assigns to input the string it formerly held, but converted to all upper-case
letters. (Numerals, punctuation, and other symbols are left unchanged.)
There is also the method, toLowerCase, which operates similarly: S.toLowerCase()
returns a string that looks like S except that all letters are translated to lower
case.
• Often, a specific character (say, the leading one) must be extracted from an
input string; use the charAt method to do this. For example,
extracts the leading character from the string, input. (Recall that the characters
in a string are indexed 0, 1, 2, etc.) As noted in Chapter 3, characters can be
saved in variables declared with data type char, such as the variable letter
seen above.
Recall from Chapter 3 that a literal character is written with single quotes
surrounding it, e.g., ’a’, or ’F’, or ’4’. Characters can be compared with the
comparison operations on integers, e.g., ’a’ == ’A’ computes to false.
The character in variable, letter, can be examined to see if it is, say, ’F’, by
the if-statement,
if ( letter == ’F’ )
{ ... }
assigns to first two a string consisting of the first two characters from string
input. Another example is
/** readAmount returns the numerical value included in the input command line
* @return the amount, converted entirely into cents; if there is
* no amount to return, announce an error and return 0. */
public int readAmount()
{ int answer = 0;
// extract substring of input line that forgets the initial character:
String s = input line.substring(1, input line.length());
s = s.trim(); // trim leading blanks from substring
if ( s.length() > 0 ) // is there a number to return?
{ double dollars cents = new Double(s).doubleValue();
answer = (int)(dollars cents * 100); // convert to all cents
}
else { JOptionPane.showMessageDialog(null,
"BankReader error: no number for transaction---zero used");
}
return answer;
}
}
270
which assigns to remainder a string that looks like input less its leading char-
acter. (Recall that length() returns the number of characters in a string.)
uses the substring method to extract from input line that subpart of it that starts
at character 1 and extends to the end of the string. The statement,
makes the fractional number, dollars cents, truncate to an integer cents amount by
using the cast, (int), into an integer. Finally, if the input line contains no number
to return, the method returns zero as its answer, since bank transactions involving
zero amounts are harmless.
Next, Figure 15 presents the output-view class. The constructor for class BankWriter
gets the address of the BankAccount object for which the BankWriter displays infor-
mation. (Remember from Chapter 5 that the address of an object can be a pa-
rameter to a method.) The BankAccount’s address is used in the paint method, at
bank.getBalance(), to fetch the account’s current balance.
A private method, unconvert, reformats the bank account’s integer value into
dollars-and-cents format. The method uses the helper object, new DecimalFormat("0.00"),
where the string, "0.00", states the amount should be formatted as a dollars-cents
amount.
Also within BankWriter are the two methods both named showTransaction. The
first of the two methods is invoked when a showTransaction message with a string
parameter and an integer parameter is sent to the BankWriter object; the second
method is used when a showTransaction message is sent with just a string parameter.
Overloading a method name is a cute “trick” and is sometimes used to great
advantage (indeed, the method name, println, of System.out is overloaded), but it
can be confusing as well. We take a closer look at overloading in Chapter 9.
We can test the two new classes in the same way that we tested the model, class
BankAccount: By using an IDE or writing a main method, we construct example input-
and output-view objects from the two classes, and we send messages that invoke all
the objects’ methods with proper and improper arguments.
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 271
...
272
3. Otherwise:
(a) If the command is ’D’, then read the amount of the deposit, make the
deposit, and display the transaction.
(b) Else, if the command is ’W’, then read the amount of the withdrawal, make
the withdrawal, and display the transaction.
(c) Else, the command is illegal, so complain.
Nested conditionals will be used to implement this algorithm, which appears in Figure
16. The Figure displays several noteworthy programming techniques:
• A separate, “start up” class, AccountManager, creates the application’s objects,
connects them together, and starts the controller. Notice how the model object
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 273
is created in the main method, before the output-view, so that the latter can be
given the address of the former:
account = new BankAccount(0);
writer = new BankWriter("BankWriter", account);
The brackets around the else-arms are absent; this is allowed when there is only
one statement in a conditional statement’s else-arm (in this case, it is another
conditional). The above format is an abbreviation for this heavily indented and
bracketed construction:
if ( TEST1 )
{ S1 }
else { if ( TEST2 )
{ S2 }
else { if ( TEST3 )
{ S3 }
else ...
}
}
Testing the controller must be done a bit differently that testing the other com-
ponents, because the controller relies on other classes to do much of the work. When
we examine Figure 5, we see that the controller depends on BankReader, BankWriter,
and BankAccount. How do we test the controller? There are two approaches:
1. Assuming that we do not have the finished forms of the other classes, we write
“dummy classes,” sometimes called stubs, to stand for the missing classes. For
example, we might use this dummy class to help us test class AccountController:
The dummy class contains only enough instructions so that the Java compiler
can process it and the controller can construct a dummy object and send mes-
sages to it. A typical dummy class is little more that the class’s specification
plus a few println and return statements. This makes it easy to write dummy
classes quickly and do tests on just of the controller.
2. You can complete one (or more) of the components used by the controller and
connect it to the controller for testing the latter. In effect, you are starting the
final development stage, testing the completed application, while you test the
controller.
This approach can make controller testing more demanding, because errors
might be due to problems in the controller’s coding or due to problems in
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 275
Exercises
1. Here are some small exercises regarding testing:
(b) Write a tester-controller that creates a BankReader object and asks the
object to read a command. The controller copies the command code and
amount into the command window.
(c) Extend the test you did on the BankAccount to include a BankWriter object.
Use the BankWriter’s showTransaction method to display the results of the
deposit and the withdrawal.
(d) Finally, write a tester that uses a BankReader to read a deposit command,
places the deposit in a BankAccount, and tells a BankWriter to display the
result.
2. Add this method to class BankAccount:
and add the input command, I dd.d, which allows the user to increase the
account’s current balance by dd.d%.
3. The constructor for class BankAccount allows a new bank account to begin with
a nonnegative value. Modify the controller, class AccountManager, so that on
startup, it asks the user for an initial value to place in the newly created bank
account.
AccountManager2 AccountController2 1
main(...) processTransactions() 2 BankAccount
BankReader 2
BankWriter
To build this architecture, we reuse all the classes we have and modify the con-
troller. The new controller, AccountManager2, will process the same input commands
as before plus these two new ones:
Here is a sample execution of the application, where the primary account gets $50.50,
the secondary account gets $10.00, and the primary account loses $30.33:
Command (P,S,D,W,Q):P
Command (P,S,D,W,Q):d 50.50
Command (P,S,D,W,Q):s
Command (P,S,D,W,Q):D10.00
Command (P,S,D,W,Q):p
Command (P,S,D,W,Q):W 30.33
Command (P,S,D,W,Q):q
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 279
Just before the user quits, the output views look like this:
The modified program uses two models, called primary account and secondary account;
each model has its own output-view, primary writer and secondary writer, respec-
tively.
It is crucial that the controller remember which of the two accounts is active and is
the target of the transactions. For this purpose, the controller uses the fields private
BankAccount account and private BankWriter writer to hold the addresses of the
account and its view that are active. The fields are initialized to the (addresses of
the) primary account objects.
Figure 18 presents the modified controller. Within processTransactions, con-
ditional statements are used to detect the new commands, P and S, and adjust the
values in the account and writer fields accordingly.
When you start this application, you will see two graphics windows appear on the
display—one for the primary account and one for the secondary account. (Perhaps
the two windows will first appear on top of each other; use your mouse to move one
of them. Or, you can use the setLocation method—f.setLocation(x ,y) positions
the upper left corner of frame f at coordinates x, y on the display.)
When the program is started, it creates the following configuration in computer
280
// fields that remember which model and view are active for transactions:
private BankAccount account;
private BankWriter writer;
// invariant: these fields must belong to the primary or secondary account
/** resetAccount changes the current account to new account and new writer */
private void resetAccount(BankAccount new account, BankWriter new writer)
{ writer.showTransaction("Inactive"); // deactivate current account
account = new account; // reset the account to the new one
writer = new writer;
writer.showTransaction("Active");
}
...
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 281
storage:
AccountManager2 a6 : AccountController2
...
BankAccount primary account == a2
BankWriter primary writer == a3
a2 : BankAccount
BankAccount secondary account == a4
int balance == 0
BankWriter secondary writer == a5
public void deposit(...){...}
public boolean withdraw(...){...}
BankReader reader == a1
public int getBalance(){...}
BankAccount account == a2
a4 : BankAccount BankWriter writer == a3
int balance == 0 main
{ ...
public void deposit(...){...}
> processTransactions();
public boolean withdraw(...){...}
}
public int getBalance(){...}
a3 : BankWriter a5 : BankWriter
BankAccount bank == a2 BankAccount bank == a4
... ...
a1 : BankReader
...
Two BankAccount and BankWriter objects appear, and the fields account and writer
hold the addresses of the objects that the user manipulates with deposits and with-
drawals. When the user selects the secondary account, the two fields receive the
values a5 and a6, respectively.
Class BankAccount has been used to generate multiple bank account objects, just
as we might use a blueprint to build several houses. In the chapters that follow, we
make good use of this principle to construct multiple objects that work in ensemble
to solve significant problems.
Exercise
Revise AccountManager2 so that it can transfer funds from the primary account to
the secondary account. The new command, >, followed by an amount, withdraws
the amount from the primary account and deposits it into the secondary account.
Next, add the command, < , which transfers money from the secondary account to
the primary one.
6.9. MORE ABOUT TESTING CLASSES AND METHODS 283
For example, we might test the withdraw method of class BankAccount from Fig-
ure 11. The method uses the class’s balance attribute, so we consider the acceptable
range of values for that attrbute, and decide that balance should always hold a non-
negative integer. So, we might test the withdraw method by initializing balance to a
nonnegative as follows:
public class BankAccount
{ private int balance = 44; // pick some value for initialization
Recall that a representation invariant is a property about a field that must always
be preserved, no matter what updates are made to the field. Such an invariant
typically states some “reasonable range of values” that a field may have. If all of
a class’s methods pledge to preserve the representation invariant, then each method
can be tested individually, where, as part of the testing, the representation invariant
is verified as preserved by the method.
For example, in class BankAccount the representation invariant for balance states
that balance’s value is always nonnegative; each method promises to preserve this
invariant. To test the deposit method, we first set balance to a nonnegative value,
so that the representation invariant is true. Then, we test deposit with an actual
parameter. After the test, we verify that deposit behaved properly and that balance
retains a nonnegative value. Similarly, the withdraw method and the class’s con-
structor method must be tested to verify that they preserve balance’s representation
invariant.
In this way, we alter our approach to testing a suite of methods—we do not worry
about the order in which the methods are invoked; instead, we validate that, regardless
of the order in which a class’s methods are invoked, all attributes’ values are safely
maintained to be acceptable to the representation invariants.
print variables’ values. Once you have seen all the values you desire, you tell the
debugger to resume execution.
Using the println technique, we can alter deposit to generate its own execution
trace as follows:
The trace information proves especially useful to understanding the order in which
methods are invoked and the values the methods receive and return.
When one method invokes another, we can adapt the “dummy class” (“stub”)
technique so that we can test one method even if the methods invoked are not yet
coded—we write “dummy” methods for the methods not yet coded. (The dummy
methods must respect the representation invariants, of course.) For example, if we
have written deposit but not the other methods of BankAccount, we might build the
following class for testing:
6.10 Summary
Here are the main points from this chapter:
6.10. SUMMARY 287
New Constructions
• conditional statement (from Figure 1):
if ( hours >= 0 )
{ JOptionPane.showMessageDialog(null,
hours + " hours is " + seconds + " seconds");
}
else { JOptionPane.showMessageDialog(null,
"ConvertHours error: negative input " + hours);
}
if ( hours < 0 )
{ String error = "convertHoursIntoSeconds error: bad hours " + hours;
JOptionPane.showMessageDialog(null, error);
throw new RuntimeException(error);
}
• switch statement:
switch ( letter )
{ case ’C’: { ...
break;
}
case ’F’: { ...
break;
}
case ’K’: { ...
break;
}
case ’R’: { ...
break;
}
default: { System.out.println("TempConverter error: bad code"); }
}
288
New Terminology
• control flow: the order in which a program’s statements execute
• control structure: a statement form that determines the control flow
• conditional statement (“if statement”): a control structure that asks a question
(test) and selects a flow of control based on the answer (e.g.,
if ( hour >= 13 )
{ answer = answer + (hour - 12); }
else { if ( hour == 0 ) ... }
as seen above; the test is hour >= 13). The possible control flows are the con-
ditional’s then-arm (e.g., answer = answer + (hour - 12)) and the else-arm
(e.g., if ( hour == 0 ) ...). When the test results in true, the then-arm is
executed; when the test results in false, the else-arm is executed.
• nested conditional: a conditional statement placed within an arm of another
conditional statement (e.g., the previous example).
• relational operator: an operator that lets us write two comparisons in one expres-
sion (e.g., the conjunction operator, &&, in letter != ’F’ && letter != ’C’).
The result from computing the expression is again true or false. The standard
relational operators are conjunction (&&), disjunction (||), and negation (!).
• exception: an error that occurs during program execution (e.g., executing 12/0
throws a division-by-zero exception). Normally, an exception halts a program.
6.10. SUMMARY 289
• accessor method: a method that merely reports the value of an object’s fields
(attributes) but does not alter any fields (e.g., getBalance in Figure 11)
• mutator method: a method that alters the values of an object’s fields (e.g.,
deposit in Figure 11)
• overloaded method name: a method name that is used to name two different
methods in the same class (e.g., showTransaction in Figure 15). When a message
is sent containing an overloaded method name, the receiver object uses the
quantity and data types of the actual parameters to select the appropriate
method to execute.
Points to Remember
• A conditional statement is written so that each of its arms are dedicated to
accomplishing a goal; the conditional’s test expression provides information
that ones uses to validate that the arms achieve the goal: arms:
if ( TEST )
{ // here, assume TEST is true
STATEMENTS1 // use the assumption to reach some goal, G
}
else { // here, assume !TEST is true
STATEMENTS2 // use the assumption to reach some goal, G
}
// regardless of outcome of TEST, goal G is achieved
290
• In the case of a fatal error that arises in the middle of an execution, a pro-
grammer can alter the control flow with a thrown exception (normally, this
terminates execution with an error message), a system exit (this always termi-
nates execution without an error message), or a premature return statement
(this forces the method to quit and immediately return to the client’s position
of invocation).
2. Consider yet again temperature conversions, and review the conversion formulas
between Celsius and Fahrenheit from the previous chapter.
Another temperature scale is Kelvin, which is defined in terms of Celsius as
follows:
K = C + 273.15
Write an application that asks the user for a temperature and its scale and asks
for a scale to which the temperature should be converted. The application does
the conversion and displays the result.
Constructor
Money(int initial amount) Construct a new object such that it holds
initial amount of money.
Attribute
private int amount the amount of money not yet converted into
change
Method
extract(int coins value): extract the maximum quantity possible of the
int coin with the value, coins value, from the
amount of money. Reduce the value of amount
accordingly, and return as the answer the num-
ber of coins that were extracted.
4. Write a model, class Mortgage, that models and monitors the money paid
towards a house mortgage. The class should match this specification:
292
Constructor
Mortgage(double p, double i, Construct a new object based on the starting
int y) principal, p, the loan’s interest rate, i, and the
number of years, y, needed to repay the loan.
(Note: interest, i, is expressed as a fraction,
e.g., 10% is 0.10.)
Attributes
private double the loan’s monthly payment, as calculated from
monthly payment the starting principal, interest rate, and dura-
tion. (See the formula following this Table.)
private double how much is left to repay of the loan
remaining principal
private double total paid the total amount of monthly payments paid so
far
Methods
makeMonthlyPayment() make one monthly payment towards the loan,
increasing the total paid and reducing the
remaining principal. (See the formula follow-
ing the Table.)
(1+i)y −1
monthlyP ayment = payment/12.0
Once you complete the model, use it in an application that lets a user submit a
starting principal, interest rate, and loan duration. The application replies with
the monthly payment. Then, each time the user presses the button on a dialog,
the application makes a monthly payment and displays the remaining principal
and the total paid so far. The user continues to press the dialog’s button until
the loan is paid in full.
“model” a moving automobile: class Auto must have internal state that re-
members (at least) the auto’s initial velocity and acceleration, and it must have
a method that returns the auto’s current position (distance).
Once you complete the model, use it in an application that lets a user submit a
starting velocity and acceleration. Then, each time the user presses the button
on a dialog, the application adds one unit to the time and displays the distance
travelled by the auto.
6. Write a model class that represents a die (that is, a cube whose sides are num-
bered 1 to 6). The class will likely have just one method, throw(), and no at-
tributes. (Hint: use Math.random to write throw.) Then, write an output-view
class that displays a die after it has been thrown. Finally, write a controller
that lets a user throw two dice repeatedly.
7. Use the class from the previous exercise to build a game called “draw the house.”
The objective is to throw a die repeatedly, and based on the outcome, draw parts
of a house. A throw of 6 lets one draw the building, a square; a throw of 5 lets
one draw the roof; a throw of 4 lets one draw the door; and a throw of 3 lets one
draw a window. (There are two windows.) The finished house looks something
like this:
/\
/ \
-----
| _ |
|x| |x|
-----
Of course, the building must be drawn before the roof, and the roof must be
drawn before the doors and windows.
In addition to the class that represents the die, write a model class that rep-
resents the state of a partially built house. Then, write an output view that
displays the house, and write a controller that enforces the rules of the game.
8. Make a version of the game in the previous Project that lets two players compete
at building their respective houses.
9. Write an interface specfication for a model class that represents a vending ma-
chine. In exchange for money, the machine distributes coffee or tea. When you
design the specification, consider the machine’s attributes first: the quantities
of coffee, tea, and excess change the machine holds. (Don’t forget to remember
the amount of money a customer has inserted!) When you design the machine’s
methods, consider the machine’s responsibilities to give coffee, tea, and change,
294
10. Write a model class that has methods for computing an employee’s weekly pay.
The methods should include
Use the model to write a payroll application that prints an employee’s pay
receipt that lists gross pay, all deductions, and net pay.
11. Write an application that lets a user move a “mouse” within a box.
---------------------------
| __ |
| / .\ |
6.12. BEYOND THE BASICS 295
| ----- |
| |
| |
| |
| |
| |
---------------------------
The input commands to the program consist of F (move forwards one mouse-
length), L (turn left), R (turn right). Write a model that remembers the position
of the mouse in the box and the direction the mouse is pointed; the model will
have methods that let the mouse move forwards and turn left and right. Then,
write an output view that draws a picture of the mouse within the box. Finally,
write a controller that transfers the commands to the model.
12. Extend the previous Project so that there are two mice in the box, and each
mouse is controlled by a player. (The players take turns moving their respective
mice.) Invent a game, e.g., one mouse tries to “catch” the other.
These optional sections develop topics related to conditional statements and class
building:
• how to reason about the correctness of a program in terms of the flows of control
within a conditional statement
!P && !Q
Indeed, here are valuable logical equivalences that simplify relational expressions:
By reading and employing these equivalences from left to right, you can minimize
the occurrences of parentheses and negations in a logical expression. For example,
the difficult-to-understand expression, !(x < 0 || (y <= 10 && y != 1)), simplifies
as follows:
if ( TEST )
{ // here, assume TEST is true
STATEMENTS1 // use the assumption to reach some goal, G
}
else { // here, assume !TEST is true
STATEMENTS2 // use the assumption to reach some goal, G
}
// regardless of outcome of TEST, goal G is achieved
That is, the then-arm’s statements can assume the logical truth of the test when the
statements execute. Similarly, the else-arm’s statements can assume the falsity of the
test. Both arms should be dedicated to completing an overall, common goal.
For example, consider this simple method, whose goal is to compute the absolute
value of an integer:
The goal of the conditional statement is to assign the absolute value of n into answer.
To validate that the goal is achieved, we annotate the conditional and study its arms:
if ( n < 0 )
// assume n < 0:
{ answer = -n; }
// goal: answer holds |n|
The assumptions derived from the test help us deduce that both arms achieve the
goal.
Here is a more significant example: Consider the crucial step in Figure 7, where
a 24-hour time is translated into a twelve-hour time—the calculation of the hours
amount. The nested conditional that does this is extracted from Figure 7:
// assume !( hour < 0 || hour > 23 || minute < 0 || minute > 59 )
// that is, !(hour < 0) && !(hour > 23) && !(minute < 0) && !(minute > 59)
// that is, hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59
if ( hour >= 13 )
{ answer = answer + (hour - 12); }
else { if ( hour == 0 )
{ answer = answer + "12"; }
else { answer = answer + hour; }
}
// goal: append correct hour to answer
Because this conditional statement is itself is nested within the else-arm of the out-
ermost conditional, we can assume that it executes only when the outer conditional’s
test computes to false. Because of the logical equivalences stated above, we uncover
that the hours value falls in the range 0..23, which is crucial to the computation that
follows.
The overall goal of the nested conditional is to append to answer the correct twelve-
hour representation of hours. We can verify the goal by considering the outcomes of
the first test:
// assume hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59
if ( hour >= 13 )
// assume that hour >= 13
// more precisely, assume hour >= 13 && hour >= 0 && hour <= 23
// that is, hour >= 13 && hour <= 23
{ answer = answer + (hour - 12); }
// goal: append correct hour to answer
298
else // assume that !(hour >= 13), that is, hour < 12
// more precisely, assume hour < 12 && hour >= 0 && hour <= 23
// that is, hour >= 0 && hour < 12
{ if ( hour == 0 )
{ answer = answer + "12"; }
else { answer = answer + hour; }
}
// goal: append correct hour to answer
The conditional’s arms are commented with the outcomes of the test. Most impor-
tantly, we may carry the assumption that held true at the beginning of the conditional
into its two arms and use it to deduce precise information about the range of values
hour might possess.
Consider the then-arm:
// assume hour >= 13 && hour <= 23
{ answer = answer + (hour - 12); }
// goal: append correct hour to answer
The assumption tells us exactly what we must know to validate that subtraction by
12 is the correct conversion to achieve the goal.
The else-arm can be analyzed similarly:
// assume hour >= 0 && hour < 12
{ if ( hour == 0 )
{ answer = answer + "12"; }
else { answer = answer + hour; }
}
// goal: append correct hour to answer
Again, the logical assumptions help us validate that for both control flows the goal
is achieved. In summary, all cases are validated, and the nested conditional achieves
its goal.
6.12. BEYOND THE BASICS 299
Logical reasoning like that seen here is a powerful tool that helps a programmer
understand control flow and validate correct behavior prior to testing. Indeed, logical
reasoning helps one write a conditional statement correctly, because the test part
of the conditional should ask exactly the precise question that gives one the logical
information needed to validate that the conditional’s arms achieve the overall goal.
Exercises
1. Use the logical equivalences stated at the beginning of the Section to simplify
these expressions so that they possess no negation operations at all. Assume
that x and y are integer variables.
public Counter()
{ count = 0; }
(a) Write a TEST of the form P && Q and show that this expression does not
compute the same result as does Q && P.
300
3. Use logical reasoning on the arms of the conditionals to validate that the fol-
lowing methods compute their goals correctly:
(a) /** max returns the larger value of its three arguments
* @param x - the first argument
* @param y - the second argument
* @param z - the third argument
* @return the largest of the three */
public int max(int x, int y, int z)
{ int big;
if ( x >= y && x >= z )
{ big = x; }
else { if ( y >= z )
{ big = y; }
else { big = z; }
}
return big;
}
(b) /** max returns the largest value of its three arguments.
* The parameters and returned result are the same as above. */
public int max(int x, int y, int z)
{ int big = x;
if ( y > big )
{ big = y; }
if ( z > big )
{ big = z; }
return big;
}
or interface specification, for methods is needed to help them integrate correctly. The
interface specifications that we used in Tables 10 and 12 in this Chapter are good
starting examples.
Unfortunately, the Java compiler does not force a programmer to write specifica-
tions like those in Tables 10 and 12. Indeed, the compiler merely requires that each
method possess a header line that lists the data types of the method’s parameters
and the data type of the returned result, if any. The Java compiler uses the header
line to ensure that every invocation of the method uses the correct quantity and data
typings of actual parameters. The compiler also checks that the result returned from
the method can be correctly inserted into the place of invocation.
But the compiler’s checks are not enough. Say that a programmer has miscoded
the deposit method from class BankAccount in Figure 11 as follows:
public void deposit(int amount)
{ balance = balance * amount; }
Although this coding violates the specification in Table 10, the Java compiler does not
notice the problem. This is a disaster—other programmers, building other classes,
will rely on Table 10 as the correct specification for class BankAccount, meaning that
the BankAccount objects they create and use are faulty.
The point is a programmer is morally bound to build a class that correctly matches
its interface specification. To remind the programmer of her obligation, in this text we
require that each method be prefixed by comments taken from the class’s specification.
Also, once the class is completed, the javadoc program can be used to generated the
Web page description for the class. (See the section, “Generating Web Documentation
with javadoc,” in Chapter 5.)
A well-written interface specification will state concisely the nature of a method’s
parameters, what the method does, and what result, if any, the method produces.
Let’s examine the specfication for method withdraw from Figure 11:
Notice the remark attached to parameter amount: It states that the actual parameter
must be a nonnegative int. This requirement of the parameter is called a precondi-
tion; the correct behavior of the method is guaranteed only when this condition holds
true for the actual parameter. A precondition is like the statement of “acceptable con-
ditions of use” that one finds in a warranty that comes with a household appliance.
(A real-life example: “This television is for indoor use only.”)
The @return comment, which describes the result value, is called the postcondition.
The clients (users) of the method trust the postcondition to state the crucial property
302
of the method’s result. The clients use the postcondition to validate their own work.
(For example, the validation of the controller, AccountManager, depends crucially on
withdraw accomplishing what is promised in its postcondition.)
When a method has no @return clause (that is, its return type is void), then the
first sentence of the comment, which describes the behavior of the method, acts as
the postcondition. The specification for method deposit is a good example:
Finally, here is a warning about typing the Java commentary for an interface
specfication: A method’s commentary is enclosed within comment markers, /** and
*/. Don’t forget the */! The following class compiles with no errors, yet it is missing
one of its methods:
class M
{ /** f’s description goes here.
* @param i - parameter info
* @return information
public int f(int i) { return i+1; }
The problem is the missing */ in the commentary for f. Because of the missing com-
ment marker, the code for f is treated as a comment that merges into g’s comments.
Hence, only g is compiled in class M. You will notice the problem only later when you
try to invoke f from another program and you receive a mysterious message from the
compiler stating that f does not exist.
Chapter 7
7.4 Nontermination
7.6 For-Statements
7.10 Recursion
7.13 Summary
Repeating an action over and over is called repetition. This Chapter introduces
techniques and applications of repetition in programming. The Chapter is organized
into three distinct parts:
7.1. REPETITION 305
• The first part, Sections 7.1-7.8, introduce the while- and for-statements for
writing standard and classic patterns of repetition, called iteration.
• The second part, Section 7.9, applies repetition in a case study of designing and
building an animation
• The third part, Sections 7.10-7.12, promotes another form of repetition, recur-
sive method invocation, as a technique for solving problems in terms of repeat-
edly solving simpler subproblems.
The first part of the Chapter is essential reading; the second is strongly recommended;
and the third can be omitted on first reading, if desired.
After studying the Chapter, the reader should be able to identify when a pro-
gramming problem should be solved by means of repetitive computation, apply the
appropriate repetitive pattern, and write the pattern in Java.
7.1 Repetition
Some jobs must be solved by repeating some step over and over:
• When replacing a flat tire with a spare, you must “place a lug nut at the end
of each bolt, and as long as the nut is loose, rotate it clockwise over and over
until it is finally secure against the wheel.”
• When searching for your favorite show on the television, you must, “while you
haven’t yet found your show, press the channel button repeatedly.”
Both of these “algorithms” rely on repetition to achieve their goals.
Computers became popular partly because a computer program will unfailingly
repeat a tedious task over and over. For example, perhaps you must know the decimal
values of the fractions (reciprocals) 1/2, 1/3, 1/4, and so on, up to 1/20. A painful
way of programming these calculations is manually repeating a division statement 19
times:
public static void main(String[] args)
{ System.out.println("1/2 = " + (1.0/2));
System.out.println("1/3 = " + (1.0/3));
System.out.println("1/4 = " + (1.0/4));
...
System.out.println("1/20 = " + (1.0/20));
}
The computer readily computes the reciprocals, but we should find a simpler way
to request the computations than the “copy and paste” coding technique above. A
better solution is built with a control structure called a while loop.
306
Step 1 initializes the variable that acts as the loop’s counter; Step 2 prints the recip-
rocals, one by one, as the loop’s counter increases by ones. Here is how the algorithm
is written in Java:
public static void main(String[] args)
{ int denominator = 2;
while ( denominator <= 20 )
{ System.out.println("1/" + denominator + " = " + (1.0 / denominator));
denominator = denominator + 1;
}
}
The while-loop prints the fractional representations of 1/2, 1/3, ..., 1/20 and stops.
Here is a more precise explanation of how the while-loop, while ( TEST ) { BODY
}, executes:
2. If TEST computes to true, then the BODY executes and the process repeats, restart-
ing at Step 1.
3. If TEST computes to false, then the BODY is ignored, and the loop terminates.
Repetition by means of a while-loop is called iteration, and one repetition of the loop’s
body is called an iteration; when the loop repeats its body over and over, we say that
it iterates.
Here is the flowchart representation of a a while-loop—it is a graph with a cycle:
TEST?
true false
BODY
Exercises
1. What will these while-loops print?
assigns 5 to i.)
(b) int countdown = 10;
while ( countdown != 0 )
{ System.out.println(i);
countdown = countdown - 1;
}
308
2. Write a while-loop to print the characters, ’a’ to ’z’, one per line. (Hint:
recall that Java allows you to initialize a variable as char c = ’a’ and to do
arithmetic with it, e.g., c = c + 1.)
3. Write a while-loop to print all the divisors of 1000 that fall within the range 2
to 50. (Recall that an integer, i, divides another integer, j, if j % i equals 0.)
(Hint: insert an if-statement in the body of the loop.)
The ellipsis in the formula suggests that we should repeatedly sum the scores of the
exams until all N scores are totalled; then we do the division. Here is an algorithm
that uses several variables, along with a while-loop, to sum the exams:
1. Assume that variable how many holds the quantity of test scores to be read.
7.3. DEFINITE ITERATION 309
2. Declare a variable total points = 0 to remember the total of all the exams,
and declare variable, count = 0, to remember how many exam scores have been
read already.
• Ask the user for the next exam score and add it to total points.
• Increment count by 1.
By reading the printed trace information, we see that at the beginning (and the
end) of each iteration, the value in total points indeed equals the sum of all the
exam scores read so far, that is,
This crucial fact is called the loop’s invariant property or invariant, for short. The in-
variant explains what the loop has accomplished with its iterations so far. Because of
their value in helping us understand a loop’s secrets, we will state invariant properties
for the loops we study.
Because of the loop’s invariant property, we know that when the loop stops with
count equal to how many, then it must be the case that total points holds all the
total points for all exams. From here, it is a small step to compute the average.
In the example, variables count and total points play crucial roles in remem-
bering the loop’s progress—the former acts as the loop’s counter, and the latter
remembers a running total.
The loop’s test evaluates to true, so execution moves into the body:
The user types 8 as the first exam score; the loop’s body revises the variables’ values:
At this point, the while-loop “restarts”—the control marker, >, returns to the
beginning of the loop, and the process repeats. Of course, the variables retain their
updated values:
Again, the test is evaluated and the result is true, so the body of the loop is entered
for yet another repetition, and the second score, 5, is read:
The loop repeats twice more, reading the last two scores, and we reach the configu-
ration where count’s cell holds 4:
This loop is an example of definite iteration, because once the loop is started, the
number of iterations is completely decided; here, it is how many iterations.
Definite iteration loops almost always use
• a test expression that examines the loop counter to see if all the iterations are
completed,
For a loop counter, count, the pattern of definite iteration often looks like this:
Exercises
1. Implement an application that computes upon exam averages:
(a) First, place the computeAverage method in a new class you write, called
class ExamStatistics. Next, write a main method whose algorithm goes
like this:
i. construct a new ExamStatistics object;
ii. construct a dialog that asks the user to enter a positive number for
the number of exams
iii. invoke the computeAverage method in the ExamStatistics object
iv. construct a dialog that shows the result returned by computeAverage.
(b) Modify computeAverage so that if its argument is a nonpositive integer,
the method shows a dialog that announces an error and returns 0.0 as its
answer.
(c) Next, write a method, computeMaxScore:
/** computeMaxScore reads a sequence test scores submitted by a user and
* returns the highest score read
* @param how_many - the quantity of test scores to read; must be nonnegative
* @return the maximum test score */
public double computeMaxScore(int how_many)
Add this method to class ExamStatistics and modify the main method
you just wrote to invoke computeMaxScore instead.
(d) Write this method and include it within class ExamStatistics:
/** computeBetterAverage computes the average of test scores submitted
* by a user _but discards_ the lowest score in the computation.
* @param how_many - the quantity of test scores to read; must be > 1
* @return the average of the best (how_many - 1) test scores */
int t = 4;
int count = 2;
while ( count <= 4 )
{ t = t * 2;
count = count + 1;
}
3. Use the pattern for definite iteration to write a loop that displays this output,
all on one line: 88 77 66 55 44 33 22 11
314
(a) uses a loop to ask the user to type four words, one word at a time, e.g.,
I
like
my
dog
(b) shows a dialog that displays the words listed in reverse order on one line,
e.g.,
dog my like I
(Note: if b > a holds true, then define product(a, b) = 1.) For example,
product(3, 6) is 3 * 4 * 5 * 6 = 360.
(c) A famous variation on iterated product is factorial; for a nonnegative
integer, m, its factorial, m!, is defined as follows:
0! = 1
n! = 1 * 2 * ... * n, for positive n
i. /** sine calculates the sine value of its argument, using the formula
* sin(x) = x - (x^3/3!) + (x^5/5!) - (x^7/7!) + ... - (x^n/19!)
* @param x - the value, in radians, whose sine is desired
* (i.e., sine(0)=0, sine(pi/2)=1, sine(pi)=0, sine(3pi/2)=-1, etc.)
* @return the sine as calculated by the formula */
public double sine(double x)
(Note: use Math.pow(a,b) to compute ab .) Compare the answers your
method produces to the ones produced by the method, Math.sin(...).
ii. /** cosine calculates the cosine value of its parameter, using the formula
* cosin(x) = 1 - (x^2/2!) + (x^4/4!) - (x^6/6!) + ... - (x^20/20!)
* @param x - the value, in radians, whose cosine is desired
* @return the cosine as calculated by the formula */
public double cosine(double x)
Assuming that the size of the bulls-eye is also set by the user, we can use the definite
iteration pattern to paint each stripe, one at a time, from the outermost to the
innermost. Three variables will be needed to control the painting:
• count, which remembers how many circles have been drawn;
• color, which remembers the color of the next circle to paint;
• diameter, which remembers the diameter of the next circle to paint.
316
Why do we require these three variables? Obviously, to paint one ring, we must know
the ring’s diameter and color. If we wish to paint multiple rings, we must remember
how many rings we have painted already, so that we know when it is time to stop.
The three variables are used in the painting algorithm:
1. Set count equal to 0, set color equal to red, and set diameter equal to the
bulls-eye’s size.
2. While count not yet equals rings (the total number of rings desired), do the
following:
• In the center of the graphics window, paint a filled circle of the appropriate
diameter with the current color.
• Revise all variables: increment count by one, make the diameter smaller
(so that the next ring is painted smaller than this one), and reset the color
(if it’s red, make it white; if it’s white, make it red).
These are the basic steps, although the details for calculating the rings’ exact positions
are omitted for the moment.
Perhaps we write the algorithm as a method, so that the method can be used at
will to paint bulls-eyes at whatever position, number of rings, and diameter that we
desire: Figure 2 displays the Java method that is parameterized on these arguments.
The method in Figure 2 is used as a helper method in the class that displays the
graphics window—see Figure 3. To use class BullsEyeWriter, we would construct a
new object such as
Exercises
1. Construct this object:
Explain why it is important that the bulls-eye’s circles are painted from the
largest to the smallest; explain why the innermost ring (a circle, actually) has
a width that is almost twice as large as all the other rings. (Hint: insert a
System.out.println(diameter) statement into the paintBullsEye method to
monitor the drawing of the rings.)
7.4 Nontermination
Recall the computeAverage method in Figure 1, which read a sequence of exam scores,
summed them, and computed their average. What happens when we invoke the
method with a negative integer, e.g., computeAverage(-1)? Indeed, the invocation
requests exam scores forever, summing the scores without ceasing, and we will see an
indefinite produced,
count = 1; total = 8
count = 2; total = 13
count = 3; total = 19
count = 4; total = 26
count = 5; total = 30
...
assuming that the user is patient enough to submit a never-ending sequence of scores!
The loop inside computeAverage iterates indefinitely because its test expression is
always true. Such behavior is called nontermination, or more crudely, infinite looping
or just “looping.” A nonterminating loop prevents the remaining statements in the
program from executing, and in this case, the method from computing the average
and returning an answer.
Although the method’s header comment tells us to supply only nonnegative pa-
rameters to computeAverage, the method might defend itself with a conditional state-
ment that checks the parameter’s value:
/** computeAverage computes the average of test scores submitted by a user
* @param how_many - the quantity of test scores to read; must be nonnegative
* @return the average of the test scores
* @throw RuntimeException, if how_many is negative */
public double computeAverage(int how_many)
320
{ if ( how_many <= 0 )
{ throw new RuntimeException("computeAverage error: negative quantity"); }
double total_points = 0.0; // the total of all test scores
int count = 0; // the quantity of tests read so far
while ( count != how_many )
{ ... see Figure 1 for the loop’s body ... }
return (total_points / how_many);
}
The initial conditional statement throws an exception to prevent looping.
Often, nontermination is an unwanted behavior, and unfortunately there is no
mechanical technique that can verify that a given loop must terminate, but the section
titled “Loop Termination,” included as an optional section at the end of the Chapter,
presents a technique that helps one prove loop termination in many cases.
If one of your Java applications appears to have infinite looping, you can terminate
it: When using an IDE, select the Stop or Stop Program button; when using the JDK,
press the control and c keys simultaneously.
Finally, we should note that a while-loop’s test can sometimes be written cleverly
so that looping becomes impossible. Consider again the loop in Figure 1, and say
that we rewrite the loop’s test so that the Figure reads as follows:
public double modifiedComputeAverage(int how_many)
{ double total_points = 0.0;
int count = 0;
while ( count < how_many )
{ String input = JOptionPane.showInputDialog("Type next exam score:");
int score = new Integer(input).intValue();
total_points = total_points + score;
count = count + 1;
System.out.println("count = " + count + "; total = " + total_points);
}
return (total_points / how_many);
}
Now, if how many receives a nonpositive value, then the loop’s test fails immediately,
and the loop executes zero iterations.
But the simple alteration is imperfect, because it allows the method to continue
its execution with an improper value for how many and compute an erroneous result:
If how many holds a negative number, then the method computes that the exam av-
erage is 0.0, which is nonsensical, and if how many holds zero, the result is even more
surprising—try modifiedComputeAverage(0) and see.
Perhaps looping is unwanted, but under no conditions do we want a method that
returns a wrong answer, either. For this reason, you should take care when altering a
while-loop’s test to “ensure” termination; the alteration might cause a wrong answer
to be computed, travel to another part of the program, and do serious damage.
7.5. INDEFINITE ITERATION: INPUT PROCESSING 321
Exercises
1. Write this method, which is is designed to execute forever:
Now, write an application that invokes it. Test the application for as long as
you have the patience to do so.
• If the user pressed Cancel, then note this and do no further computation
within the loop;
• else the user entered an exam score, so add it to the total and add one to
the count of exams read.
To write the loop’s test expression, we will use a boolean variable, processing, to
remember whether the user has pressed the Cancel button, signalling the desire to
quit. This extra variable gives us a clever way of writing the loop:
This pattern was used in Figure 4 and can be profitably used for most input-processing
applications. The loop is an example of indefinite iteration, because when the loop
starts, it is not decided how many iterations will occur until termination. Note that
indefinite iteration is different from nontermination—assuming that there are finitely
many input transactions, the loop will terminate!
Exercises
1. Explain what this method does:
int total = 0;
while ( processing )
{ String s = JOptionPane.showInputDialog("Type an int:");
int i = new Integer(s);
if ( i < 0 )
{ processing = false; }
else { total = total + i; }
}
JOptionPane.showMessageDialog(null, total);
}
The user types the lines, one at a time, into input dialogs. When the user types
presses Cancel or when the user presses just the Enter key by itself (with no
text typed), the program prints in the command window all the lines the user
has typed and halts. (Hint: You can save complete lines of text in a string
variable as follows:
String s = "";
...
s = s + a_line_of_text + "\n";
String s = "abcde";
int i = s.length();
String s = "abcde";
char c = s.charAt(3);
1. Set index to 0.
2. While ( c is not yet found, and there are still unexamined characters within s
), do the following:
The loop’s test consists of two boolean expressions, combined by conjunction (&&),
and the loop terminates when either of the conjuncts becomes false.
This algorithm is realized in Figure 5.
The loop’s invariant property tells us how the loop operates: As long as found is
false, c is not found in the searched prefix of s; when found becomes true, the search
has succeeded.
The loop might terminate one of two ways:
• !found becomes false, that is, variable found has value true. By Clause (1) of
the invariant, we know that c is found at position index in s.
• !found stays true, but index < s.length() becomes false. By Clause (2) of
the invariant, we know that c is not in the entire length of string s.
These facts are crucial to returning the correct answer at the end of the function;
study the above until you understand it thoroughly.
A string is a kind of “collection” or “set” of characters. In the general case, one
searches a set of items with the following pattern of while-loop:
boolean item_found = false;
DETERMINE THE FIRST ‘‘ITEM’’ TO EXAMINE FROM THE ‘‘SET’’;
while ( !item_found && ITEMS REMAIN IN THE SET TO SEARCH )
{ EXAMINE AN ITEM;
if ( THE ITEM IS THE DESIRED ONE )
{ item_found = true; }
else { DETERMINE THE NEXT ITEM TO EXAMINE FROM THE SET; }
}
326
• !item found evaluates to false. This means the search succeeded, and the
desired item is the last ITEM examined.
• ITEMS REMAIN evaluates to false. This means the search examined the entire
set and failed to locate the desired item.
The search through the set, {n/2, (n/2) - 1, ..., 3, 2}, terminates if we find a divisor
that produces a remainder of 0 or if we search the entire set and fail. Figure 6 shows
the completed method.
Exercises
1. Modify method findChar in Figure 3 so that it locates the rightmost occurrence
of character c in string s. Remember to revise the loop’s invariant.
328
7.6 For-Statements
Definite-iteration loops pervade computer programming. When we use this pattern
of definite iteration,
there is a special loop statement in Java, called a for-statement, that we can use to
tersely code the above pattern; it looks like this:
The semantics of the for-statement is exactly the semantics of the definite iteration
pattern, but there is a small technical point: The scope of the declaration of i extends
only as far as the for-statement’s body—the statements following the for-statement
cannot examine i’s value. If it is important that i’s value be available after the loop
terminates, then an extra declaration must be prefixed to the loop:
int i;
for ( i = INITIAL VALUE; TEST ON i; INCREMENT i; )
{ EXECUTE LOOP BODY; }
// because i is declared before the loop starts, i’s value can be read here
Here is the for-loop that corresponds to the while-loop we saw at the beginning
of this Chapter, which prints the decimal values of the reciprocals, 1/2 to 1/20:
int denominator = 2;
while ( denominator <= 20 )
{ System.out.println("1/" + denominator + " = " + (1.0 / denominator));
denominator = denominator + 1;
}
There are two advantages in using the for-statement for definite iteration:
• The for-statement suggests to the reader that the loop is a definite iteration.
Exercises
1. Rewrite the computeAverage method in Figure 1 so that it uses a for-statement.
4. Rewrite into for-loops the while-loops you wrote in the answers to the Exercises
for the “Definite Iteration” Section
The “for” keyword in the algorithm suggests that a for-statement is the best way to
write the required loop. Similarly, printing the multiplications, i * 0, i * 1, ... i *
4, can be done by varying the second operand over the range, 0..4:
The completed algorithm uses its outer loop to count and print the rows of mul-
tiplications and uses its inner loop to print the individual multiplications on each
row:
Note the uses of print and println to format the rows of the table.
7.7. NESTED LOOPS 331
Drawing a Chessboard
How might we paint an n-by-n chessboard in a graphics window?
This task is also a table-printing problem, where the “values” in the table are red
and white squares. To understand which squares should be red and which should be
white, consider this numbering scheme for the squares, which was suggested by the
multiplication table seen earlier:
0, 0 0, 1 0, 2 ... 0, n-1
1, 0 1, 1 1, 2 ... 1, n-1
...
n-1, 0 n-1, 1 n-1, 2 ... n-1, n-1
Assuming that the board’s upper-left corner (the one numbered by 0,0) is a red square,
then it is easy to calculate the color of every square: If the square’s number is i,j,
then the square is red when i + j is even-valued; otherwise, it is white (when i + j
is odd-valued).
We write the algorithm for painting the board by imitating the algorithm for
printing the multiplication table:
• for j varying from 0 to n-1, do the following: paint the square numbered
i, j: If i + j is even-valued, paint a red square; otherwise, paint a white
square.
332
Alphabetizing a String
When a set of items must be arranged or reordered, nested loops often give a solu-
tion. Given a string, s, say that we must construct a new string that has all of s’s
characters but rearranged in alphabetical order. For example, if s is butterfly, then
its alphabetized form is beflrttuy.
The algorithm for alphabetization will copy the characters, one by one, from string
s into a new, alphabetized string, which we call answer. Here is an initial algorithm:
2. Working from left to right, for each character in s, copy the character into its
proper position in answer.
7.7. NESTED LOOPS 333
The use of “for” in Step 2 suggests that a for-statement might be used to examine
and extract the characters in s, say, as s.charAt(0), s.charAt(1), and so on:
answer = "";
for ( int i = 0; i != s.length(); i = i + 1 )
// invariant: characters at indices 0..i-1 have been inserted into answer
{ insertAlphabetically(s.charAt(i), answer); }
The body of the loop contains a helper method, insertAlphabetically, which will
place its first argument, the character, into the correct position within its second
argument, answer, so that answer remains alphabetized.
What is the algorithm for insertAlphabetically? Let’s consider the general
probem of inserting a character, c, into its proper position in an alphabetized string,
alpha. This is in fact a searching problem, where we search alpha from left to right
until we find a character that appears later in the alphabet than does c. We adapt
the searching pattern for loops seen earlier in this Chapter:
2. While ( searching for c position and there are still characters in alpha to
examine ), do the following
3. Insert c into alpha in front of the character at position index within alpha.
Figure 8 shows the final versions of the two methods developed for generating the
complete, alphabetized string. The public method, alphabetize, uses a for-statement
to systematically copy each character in the orginal string into the alphabetized string.
Helper method insertAlphabetically uses a searching loop to find the correct
position to insert each character into the alphabetized string. When the searching
loop finishes, local variable index marks the position where the character should be
inserted. The insertion is done by the last statement within insertAlphabetically,
which makes clever use of the substring method:
return alpha.substring(0, index) + c + alpha.substring(index, alpha.length());
That is, alpha.substring(0, index) is the front half of string alpha, from character
0 up to character index, and alpha.substring(index, alpha.length()) is the back
half of alpha, from character index to the end.
Note also within insertAlphabetically that the <= operation is used to compare
two characters—for example, ’a’ <= ’b’ computes to true but ’b’ <= ’a’ is false.
334
Exercises
1. Write nested loops that generate the addition table for 0+0 up to 5+5.
0 0
1 0 1 1
2 0 2 1 2 2
3 0 3 1 3 2 3 3
0 3 0 2 0 1 0 0
1 3 1 2 0 1
2 3 2 2
3 3
int i = 2;
while ( i != 0 );
{ i = i - 1; }
fails to terminate because the semicolon after the test causes the Java compiler to read
the code as while ( i != 0 ) {}; { i = i - 1; }—the loop has an empty body.
Problems also arise when a loop’s test is carelessly formulated. For example, this
attempt to compute the sum, 1 + 2 + ... + n, fails,
int total = 0;
int i = 0;
while ( i <= n )
{ i = i + 1;
total = total + i;
}
because the loop iterates one time too many. This form of error, where a loop iterates
one time too many or one time too few, is commonly made, so be alert for it when
testing the loops you write.
A related problem is an improper starting value for the loop counter, e.g.,
336
int total = 0;
int i = 1;
while ( i <= n )
{ i = i + 1;
total = total + i;
}
double d = 0.0;
while ( d != 1.0 )
{ d = d + (1.0 / 13);
System.out.println(d);
}
should terminate in 13 iterations but in reality does not due to computer arithmetic,
where fractional numbers like 1/13 are imperfectly represented as fractions in decimal
format.
Formulating test cases for loops is more difficult than formulating test cases for
conditionals, because it is not enough to merely ensure that each statement in the
loop body is executed at least once by some test case. A loop encodes a potentially
infinite number of distinct execution paths, implying that an infinite number of test
cases might be needed. Obviously, no testing strategy can be this exhaustive.
In practice, one narrows loop testing to these test cases:
• Which test cases should cause the loop to terminate with zero iterations?
• Which test cases should cause the loop to terminate with exactly one iteration?
• Which “typical” test cases should cause the loop to iterate multiple times and
terminate?
• Which test cases might cause the loop to iterate forever or produce undesirable
behavior?
Test cases of all four forms are applied to the loop code.
One more time, this attempt to compute the summation 1 + 2 + ... + n,
7.8. WRITING AND TESTING LOOPS 337
might be tested with n set to 0 (which we believe should cause immediate termination),
set to 1 (should cause termination in one iteration), set to, say, 4 (a typical case that
requires multiple iterations), and set to a negative number, say, -1, (which might cause
unwanted behavior). These test cases quickly expose that the test expression forces
termination one iteration too soon. Subtle errors arise when a loop’s test expression
stops the loop one iteration too soon or one iteration too late; testing should try to
expose these “boundary cases.”
A more powerful version of loop testing is invariant monitoring: If you wrote an
invariant for the loop, you can monitor whether the invariant is holding true while the
loop iterates. To do this, insert inside the loop one or more println statements that
display the values of the variables used by the loop. (Or, use an IDE to halt the loop
at each iteration and display the values of its variables.) Use the variables’s values to
validate that the loop’s invariant is holding true. If the invariant is remaining true,
this gives you great confidence that the loop is making proper progress towards the
correct answer.
For example, consider Figure 1, which displays a loop that sums a sequence of
exam scores. The loop’s invariant,
tells us what behavior to observe at the start (and the end) of each loop iteration.
The println statement placed at the end of the loop in Figure 1 lets us verify that
the invariant property that we believed to be true is in fact holding true during the
execution.
If we monitor a loop’s invariant, like the one in Figure 1, and we find that the
invariant goes false at some interation, this is an indication that either the loop’s code
or the invariant is incorrectly written. In the former case, repair is clearly needed; in
the latter case, we do not understand what our loop should do and we must study
further.
Although loop testing can never be exhaustive, please remember that any testing
is preferable to none—the confidence that one has in one’s program increases by the
number of test cases that the program has successfully processed.
338
(Although the printed page cannot show it, the red ball is travelling from side to side
within the frame.)
A program written in the Model-View-Controller architectural style does best:
The animation is modelled, in this case, by objects that represent the ball and box. A
view paints the model’s current state on the display. Finally a controller monitors the
passage of time and tells the model when to update the ball’s position and repaint it
on the display.
Figure 9 shows the architecture of the animation program. The controller contains
a loop that executes an iteration for each unit of time that passes. At each unit of
time, the loop’s body tells the model to update its state, and it tells the view to
repaint the model; the repeated paintings look like movement. The model and view
have methods that respond to the controller’s requests. Also, the view must ask the
model for its current state each time the model must be painted.
Recall once more the steps we take to design and solve a programming problem:
1. State the program’s behavior, from the perspective of the program’s user.
2. Select a software architecture that can implement the behavior.
3. For each of the architecture’s components, specify classes with appropriate at-
tributes and methods.
7.9. CASE STUDY: BOUNCING BALL ANIMATION 339
Controller
run() View
{ while (true) paintModel()
{ wait one time unit; { model.getState();
model.updateState(); repaint();
view.paintModel(); }
}
}
Model
getState()
updateState()
5. Integrate the classes into the architecture and test the system.
Steps (1) and (2) are not a challenge for building the moving-ball animation: The
behavior of the animation has already been indicated—there is nothing for the user
to do but watch—and the architecture in Figure 9 is a good start towards implement-
ing the desired behavior. The architecture shows that the application has distinct
model, view, and controller subassemblies, so we develop each subassembly separately,
beginning with the model.
We begin with the model subassembly of the animation—What are the model’s com-
ponents? They are of course the ball and the box in which the ball moves. Our first
attempt at specifying the model might place ball and box together in one class, like
this:
340
2. Ask the box that contains the ball whether the ball has come into contact
with one of the box’s horizontal or vertical walls; if it has, then change the
ball’s direction. (For example, if the ball makes contact with a horizontal wall,
then its horizontal direction must reverse; this is done by negating the ball’s
horizontal velocity.)
Because class MovingBall depends on class Box, we test the classes together,
say, by writing a testing program that constructs a box and places the ball in the
middle of the box. Then, we tell the ball to move and we print its new position. Of
course, we should move the ball until it comes into contact with a wall so that we
can see how the ball’s direction changes as a result. Here is some testing code:
Box box = new Box(50); // 50 pixels by 50 pixels in size
MovingBall ball = new MovingBall(25, 25, 10, box); // radius is 10 pixels
while ( true )
{ ball.move(1); // 1 unit of elapsed time
System.out.println("x = " + ball.xPosition()
+ "; y = " + ball.yPosition());
}
344
AnimationWriter
paintComponent(Graphics g)
MovingBall BallWriter { box.paint(g);
(see Table 10) paint(Graphics g) ball.paint(g);
}
Box BoxWriter
(see Table 11) paint(Graphics g)
When the animation must be painted, an AnimationWriter is asked to paint the entire
picture on a panel. This class asks the BoxWriter to paint the box and it asks the
BallWriter to paint a ball. The latter two classes query the accessor methods of their
respective model components for the state of the box and ball.
There is little more to specify about the view classes, so we study their codings.
Figure 14 presents class AnimationWriter; notice how this class gives its graphics
pen to class BoxWriter and then to class BallWriter, presented in Figure 15, so
that the box and ball are painted on the window with which the graphics pen is
associated.
We can test the view classes with the model we have already built and tested.
Figure 16 gives the coding of this loop and also the coding of the start-up class that
contructs all the animation’s objects.
The while-loop in the run method of class BounceController is intended not to
terminate, hence its test is merely true. To present an illusion of movement, the same
7.9. CASE STUDY: BOUNCING BALL ANIMATION 345
import java.awt.*;
/** BallWriter displays a moving ball */
public class BallWriter
{ private MovingBall ball; // the (address of the) ball object displayed
private Color balls color; // the ball’s color
technique from motion pictures is used: Method delay pauses the program slightly
(here, 20 milliseconds, but this should be adjusted to look realistic for the processor’s
speed) before advancing the ball one more step. The method uses a built-in method,
Thread.sleep, which must be enclosed by an exception handler. These details are
unimportant, and the delay method may be copied and used whereever needed.
The moving-ball animation is a good start towards building more complex anima-
tions, such as a billiard table or a pinball machine. Indeed, here is a practical tip: If
you are building a complex animation, it helps to build a simplistic first version, like
the one here, that implements only some of the program’s basic behaviors. Once the
first version performs correctly, then add the remaining features one by one.
For example, if our goal is indeed to build an animated pinball machine, we should
design the complete pinball machine but then design and code an initial version that
is nothing more than an empty machine (a box) with a moving pinball inside it. Once
the prototype operates correctly, then add to the machine one or more of its internal
“bumpers” (obstacles that the ball hits to score points). Once the pinball correctly
hits and deflects from the bumpers, then add the scoring mechanism and other parts.
Each feature you add to the animation causes you to implement more and more of
the attributes and methods of the components. By adding features one by one, you
will not be overwhelmed by the overall complexity of the animation. The Exercises
that follow give a small example of this approach.
Exercises
1. Execute the moving ball animation. Occasionally, you will observe that the
ball appears to bounce off a wall “too late,” that is, the ball changes direction
after it intersects the wall. Find the source of this problem and suggest ways to
improve the animation.
2. Revise the moving-ball animation so that there are two balls in the box. (Do
not worry about collisions of the two balls.)
3. If you completed the previous exercise, rebuild the animation so that a colli-
sion of the two balls causes both balls to reverse their horizontal and vertical
directions.
4. Place a small barrier in the center of the box so that the ball(s) must bounce
off the barrier.
7.10 Recursion
No doubt, you have seen a “recursive picture,” that is, a picture that contains a
smaller copy of itself that contains a smaller copy of itself that contains.... (You can
348
simulate this by hanging two large mirrors on opposite walls, standing in the middle,
and looking at one of the mirrors.) It is self-reference that characterizes recursion,
and when a method contains a statement that invokes (restarts) itself, we say that
the method is recursively defined.
In the bank-accounting example in Chapter 6. we used recursion to restart a
method, but now we study a more sophisticated use of recursion, where a complex
problem is solved by solving simpler instances of the same problem.
Here is an informal example of recursive problem solving: How do you make a
three-layer cake? A curt answer is, “make a two-layer cake and top it with one more
layer!” In one sense, this answer begs the question, because it requires that we know
already how to make a cake in order to make a cake, but in another sense, the answer
says we should learn how to make first a simpler version of the cake and use the result
to solve the more complex problem.
If we take seriously the latter philosophy, we might write this recipe for making a
cake with some nonzero number of layers, say, N+1:
To make an (N+1)-layer cake, make an N-layer cake and top it with one more layer.
The reciple simplifies (N+1)-layer cake making into the problem of N-layer cake making.
But something is missing—where do we begin cake making? The answer is startlingly
simple:
350
The solution to the original problem becomes clearer—to make a three-layer cake,
we must first make a two layer cake (and then top it with one more layer); but to
make a two-layer cake, we must first make a one-layer cake (and then top it with one
more layer); but to make a one-layer cake, we must make a zero-layer cake (and then
top it with one more layer); and we make a “zero-layer cake” from just an empty
pan. Perhaps this explanation is a bit wordy, but all the steps are listed. A diagram
displays the steps more pleasantly:
make a 3-layer cake
8
1 make a 2-layer cake
and top it with one more layer 7
2 make a 1-layer cake
and top it with one more layer 6
3 make a 0-layer cake
and top it with one more layer 5
4 place an empty cake pan on the table
By following the arrows, we understand how the problem of making a multi-layer case
is decomposed into simpler problems (see the downwards arrows) and the result is
assembled from from the solutions—the pan and layers (see the upwards arrows).
The strategy of solving a task by first solving a simpler version of the task and
building on the simpler solution is called problem solving by recursion or just recursion,
for short. When we solve a problem by recursion, we can approach it as a task of
writing simultaneous algebraic equations, like these, for cake making:
bakeACake(0) = ...
bakeACake(N + 1) = ... bakeACake(N) ..., where N is nonnegative
The first equation states how a 0-layer cake is made, and the second explains how an
N+1-layer cake is made in terms of making an N-layer one. Based on the narrative for
cake making, we would finish the equations like this:
The algorithm can be used to generate the steps for a 3-layer cake:
bakeACake(3)
=> bakeACake(2)
and top it with one more layer
=> (bakeACake(1)
and top it with one more layer)
7.10. RECURSION 351
A recursive algorithm can be reformatted into a Java method that uses recursion.
Perhaps there is a Java data type, named Cake, such that new Cake() constructs a
0-layer cake. Perhaps Cake objects have a method, named addALayer, which places
one more layer on a cake. For fun, a version of class Cake is displayed in Figure 17.
Now, we can use class Cake to reformat the two algebraic equations for cake
making into the Java method displayed in Figure 18.
Now, the assignment,
Cake c = bakeACake(0);
because this triggers bakeACake(2), which invokes bakeACake(1), which starts bakeACake(0).
The last invocation constructs a new cake object, which is then topped by three layers
352
bakeACake
{ int layers == 3 Cake result == ?
> if ( layers == 0 )
{ result = new Cake(); }
else { result = bakeACake(layers - 1);
result.addALayer();
}
return result;
}
The test marked by >1??? computes to false, meaning that the next step is a
7.10. RECURSION 353
recursive invocation:
bakeACake
{ int layers == 3 Cake result == ?
. . .
else { > result = bakeACake(layers - 1);
result.addALayer();
}
return result;
}
bakeACake
{ int layers == 3 Cake result == ?
. . .
else { > result = AWAIT RESULT
result.addALayer();
}
return bakeACake
} Cake result == ?
{ int layers == 2
> if ( layers == 0 )
{ result = new Cake(); }
else { result = bakeACake(layers - 1);
result.addALayer();
}
return result;
}
This shows that a recursive method invocation works like any other invocation: actual
parameters are bound to formal parameters, and a fresh copy of the method’s body
executes.
this configuration:
bakeACake
{ int layers == 3 Cake result == ?
bakeACake
{ int layers == 2 Cake result == ?
bakeACake
{ int layers == 1 Cake result == ?
bakeACake
{ int layers == 0 Cake result == ?
> if ( layers == 0 )
{ result = new Cake(); }
else { result = bakeACake(layers - 1);
result.addALayer();
}
return result;
}
Because the conditional’s test evaluates to true, the conditional’s then-arm constructs
a new Cake object and places its address, a1, in the local variable, result.
a1 : Cake
bakeACake
{ int layers == 3 Cake result == ?
bakeACake
{ int layers == 2 Cake result == ?
bakeACake
{ int layers == 1 Cake result == ?
bakeACake
{ int layers == 0 Cake result == a1
. . .
> return result;
}
7.10. RECURSION 355
The 0-layer cake is returned as the result, and the activation record for the completed
invocation disappears.
a1 : Cake
bakeACake
{ int layers == 3 Cake result == ?
bakeACake
{ int layers == 2 Cake result == ?
bakeACake
{ int layers == 1 Cake result == a1
. . .
> result.addALayer();
}
return result;
}
Next, result.addALayer() adds a layer to the cake, and the one-layer cake is returned
as the result:
a1 : Cake
bakeACake
{ int layers == 3 Cake result == ?
bakeACake
{ int layers == 2 Cake result == a1
. . .
> result.addALayer();
}
return result;
}
This process repeats until the result variable at the initial invocation receives the
cake object at address a1 and places the third layer on it.
356
The example shows how each recursive invocation decreases the actual parameter
by 1, halting when the parameter has 0. When an invoked method returns its result,
the result is used by the caller to build its result. This matches our intuition that
recursion is best used to solve a problem in terms of solving a simpler or smaller one.
Admittedly, the cake-making example is contrived, and perhaps you have already
noticed that, given class Cake, we can “make” an N-layer cake with this for-loop:
But the style of thinking used when writing the for-loop is different than the style
used when writing the recursively defined method.
The loop in the previous paragraph should remind you that an incorrectly written
repetitive computation might not terminate—this holds true for loops and it holds
true for recursively defined methods as well. It is not an accident that the cake-making
example used recursive invocations with an argument that counted downward from
an nonnegative integer, N, to 0. As a rule:
A recursively defined method should use a parameter whose value decreases at each
recursive invocation until the parameter obtains a value that stops the recursive
invocations.
The “counting-down” pattern suggested above will protect us from making the foolish
mistake of writing a recursively defined method that restarts itself forever.
• You must visit three cities, a, b, and c, next week. What are the possible orders
in which you can visit the cities, and which of these orders produces the shortest
or least-expensive trip?
• You must complete three courses to finish your college degree; what are the
possible orders in which the courses might be taken, and which ordering allows
you to apply material learned in an earlier course to the courses that follow?
7.11. COUNTING WITH RECURSION 357
• Three courses that are taught at the same hour must be scheduled in three
classrooms. What are the possible assignments of courses to classrooms, and
which assignments will accommodate all students who wish to sit the courses?
• A printer must print three files; what are the orders in which the files can be
printed, and which orderings ensure that the shorter files print before the longer
files?
It is valuable for us to know how to generate and count permutations. To study
this problem, say that we have n distinct letters, and we want to count all the per-
mutations of the letters. The following observation holds the secret to the solution:
By magic, say that we already know the quantity of permutations of n-1 distinct
letters—say there are m of them, that is, there are distinct words, word 1, word
2, ..., word m, where each word is n-1 letters in length.
Next, given the very last, nth letter, we ask, “how many permutations can we
make from the m words using one more letter?” The answer is, we can insert the
new letter it into all possible positions in each of the m words: We take word
1, which has length n-1, and we insert the new letter in all n possible positions
in word 1. This generates n distinct permutations of length n. We do the same
for word 2, giving us n more permutations. If we do this for all m words, we
generate m sets of n permutations each, that is, a total of n * m permutations
of length n. This is the answer.
Finally, we note that when we start with an alphabet of one single letter, there is
exactly one permutation.
The above insights tell us how to count the quantity of permutations:
For an alphabet of just one letter, there is just one permutation:
number_of_permutations_of(1) = 1
If the alphabet has n+1 letters, we count the permutations made from n letters, and
we use the technique described above to count the permutations generated by the
addition of one more letter:
number_of_permutations_of(n+1) = (n + 1) * number_of_permutations_of(n)
These two equations define an algorithm for computing the quantity of permu-
tations. We can use this algorithm to quickly count the permutations of 4 distinct
letters:
number_of_permutations_of(4)
= 4 * number_of_permutations_of(3)
= 4 * ( 3 * number_of_permutations_of(2) )
= 4 * ( 3 * ( 2 * number_of_permutations_of(1) ))
= 4 * 3 * 2 * 1 = 24
358
Calculations with this algorithm quickly convince us that even small alphabets gen-
erate huge quantities of permutations.
Not surprisingly, permutation counting occurs often in probability and statistics
theory, where it is known as the factorial function. It is traditional to write the
factorial of a nonnegative integer, n, as n!, and calculate it with these equations:
0! = 1
In practice, many counting problems are best solved with recursive techniques.
If the recursive solution has a simple form, like those seen in the cake-making and
factorial examples, then it may be possible to rewrite the recursive implementation
into a loop. But the next section shows counting problems where solutions with
multiple recursions are needed; such solutions do not always convert to loop code.
that is, the number of flies produced by a newly hatched fly is just the fly itself.
These two equations generate a Java method that contains two recursions:
public int fliesAtGeneration(int n)
{ int answer;
if ( n < 0 )
{ throw new RuntimeException("error: negative argument"); }
else { if ( n == 0 ) // is it a newly hatched fly?
{ answer = 1; }
else { int first_egg_produces = fliesAtGeneration(n - 1);
int second_egg_produces = fliesAtGeneration(n - 1);
answer = first_egg_produces + second_egg_produces + 1;
}
return answer;
}
7.11. COUNTING WITH RECURSION 361
When executing the innermost else-clause, the computer computes the first recursion
completely to its integer result before it starts the second recursive invocation. Be-
cause each recursive invocation uses an argument that is smaller than the one given
to the caller method, all the recursions will terminate.
Now that we have used recursive problem solving, we can readily see that the two
recursions in the method’s innermost else-clause can be replaced by one:
This simplifies the method and even allows us to rewrite the method’s body into a
loop, if we so desire.
Although the fly-counting example is a bit contrived, there is a related counting
problem that is not: the Fibonacci function. The Fibonacci number of a nonnegative
integer is defined in the following way:
Fib(0) = 1
Fib(1) = 1
This recursively defined algorithm was proposed in the 13th century for counting
the number of pairs of rabbits produced by one initial pair, assuming that a pair of
rabbits takes one month to mature and from then on the pair produces one more pair
of rabbits every month thereafter! Since that time, Fibonacci numbers have arisen in
surprising places in problems in biology, botany, and mathematics.
The algorithm for the Fibonacci function has an easy coding as a method that
uses two recursions in its body. It is a fascinating project to rewrite the method so
that it uses only one recursion.
Exercises
1. Write an application that prints the values of 3!, 6!, 9!, ..., 18!.
2. Remove from the factorial method the first conditional, the one that tests n <
0 || n > 20. Then, try to compute factorial(20), factorial(21), factorial(99),
and factorial(-1).
3. With a bit of thought, you can use a while-loop to program the recursive equa-
tions for factorial. Do this.
4. Implement the Fibonacci function with a method that contains two recursions,
and try your function with the actual parameters 20; 30; 35; 40. Why does the
computation go so slowly for the test cases?
362
(Incidentally, the Fibonacci function was proposed in the 13th century for count-
ing the number of pairs of rabbits produced by one initial pair, assuming that a
pair of rabbits takes one month to mature and from then on the pair produces
one more pair of rabbits every month thereafter!)
add(0, b) = b
add(a, b) = add(a-1, b) + 1, when a > 0
A(0, n) = n + 1
A(m, 0) = A(m-1, 1), when m > 0
A(m, n) = A(m-1, A(m, n-1)), when m > 0 and n > 0
8. Say that a word is sorted if all its letters are distinct and are listed in alphabetical
order, e.g., "bdez" is sorted but "ba" and "bbd" are not. Write a recursive
algorithm that calculates the number of sorted words of length n or less that
one can create from n distinct letters.
7.12. DRAWING RECURSIVE PICTURES 363
9. Solve this modified version of the fly-generation problem: Say that an “ordi-
nary” fly lays exactly two eggs. One of the eggs produces another “ordinary”
fly, but the other egg produces a “queen” fly, which can lay, for every generation
thereafter, two eggs that hatch into ordinary flies.
Starting from one ordinary fly, how many flies are produced from it in n gener-
ations? Starting from one queen fly, how many flies are produced from it in n
generations?
(Hint: You will need one recursive algorithm that counts the flies produced
from one ordinary fly and one recursive algorithm that counts the number of
children flies produced from one queen fly.)
You might give a painter a bucket of paint, a brush, and these recursive instructions:
Paint a slightly smaller egg-picture in the background and next paint a big egg in the
front!
More precisely stated, the algorithm goes as follows:
1. First, paint a completed an egg-picture, sized slightly smaller than the desired
size of the final picture. (If this makes the background eggs have zero size, then
don’t bother with it.)
364
2. Next, paint the largest egg, of the desired size, in the foreground.
3. Finally, draw a border around the largest egg and its background.
The work is done by method paintEggField, which paints two triangular fields of
eggs, one to the left rear and one to the right rear, of a lead egg. The recursively
defined method, paintEggField, uses parameter layer to count downwards by one
each time the rear layers of eggs are painted. Because the left rear egg field is painted
after the right rear field, the left field’s eggs rest on the top of the right field’s.
Exercises
1. Revise class RecursivePictureWriter by removing the border around each pic-
ture; next, move the eggs so that they rest in the left corner of the picture rather
than the right corner.
7.12. DRAWING RECURSIVE PICTURES 365
2. Write a class that generates a circle of diameter 200 pixels, in which there is
another circle of 0.8 size of the first, in which there is another circle of 0.8 size
of the second, ..., until the circles shrink to size 0.
3. To better understand class EggFieldWriter, do the following. First, replace
method paintAnEgg with this version, which draws a transparent egg:
Retry EggFieldWriter.
4. Write a recursive definition, like that for the factorial and Fibonacci functions,
that defines precisely how many eggs are drawn in an egg field of n layers. How
many eggs are drawn by the output view in Figure 6?
7.13 Summary
This chapter presented concepts about repetition:
7.13. SUMMARY 367
New Constructions
• while-statement (from Figure 1):
int total = 0;
int count = 0;
while ( count != n )
{ System.out.println("count = " + count + "; total = " + total);
count = count + 1;
total = total + count;
}
• for-statement:
int total = 0;
for ( int count = 1; count <= n; count = count + 1 )
{ total = total + count;
System.out.println("count = " + count + "; total = " + total);
}
New Terminology
• loop: a statement (or sequence of statements) that repeats computation, such
as a while-statement
• iteration: repetitive computation performed by a loop; also refers to one execu-
tion of a loop’s body
• definite iteration: iteration where the number of the loop’s iterations is known
the moment the loop is started
• indefinite iteration: iteration that is not definite
• nontermination: iteration that never terminates; also called “infinite looping”
• invariant property: a logical (true-false) fact that holds true at the start and at
the end of each iteration of a loop; used to explain the workings of a loop and
argue the loop’s correctness
• loop counter: a variable whose value remembers the number of times a loop has
iterated; typically used in the loop’s test to terminate iteration. Also called a
loop control variable.
• recursive definition: a method that invokes itself, directly or indirectly; used to
solve a problem by recursive invocations to first solve simpler versions of the
problem and next combine the results to compute overall result.
7.13. SUMMARY 369
Points to Remember
• While-loops are used in two forms:
– definite iteration, where the number of times the loop repeats is known
when the loop is started. A standard pattern for definite iteration reads,
int count = INITIAL VALUE;
while ( TEST ON count )
{ EXECUTE LOOP BODY;
INCREMENT count;
}
where count is the loop control variable. The for-statement is used to
tersely code the above pattern:
for ( int count = INITIAL VALUE; TEST ON count; INCREMENT count; )
{ EXECUTE LOOP BODY (DO NOT ALTER count); }
• Inside the design of every loop is an invariant property, which states what the
loop is accomplishing as it progresses towards completion. When you design a
loop, you should also write what you believe is the loop’s invariant property;
this clarifies your thinking and provides excellent documentation of the loop.
• Use a recursively defined method when a problem is solved by using the solution
to a slightly simpler/smaller-valued version of the same problem. Write the
370
2. Write an application that reads a series of nonnegative integers and prints the
list of all the nonzero divisors of the input integer. (Recall that b is a divisor of
a if a divided by b yields a remainder of zero.) The program terminates when
a negative integer is typed; it should behave like this:
3. Write an application that calculates the odds of a person winning the lottery
by guessing correctly all m numbers drawn from a range of 1..n. The formula
that calculates the probability is
probability = m! / product((n-m)+1, n)
7.14. PROGRAMMING PROJECTS 371
(Footnote: Here is the rationale for the probability formula. Consider the task
of matching 3 chosen numbers in the range 1..10; the chances of matching on the
first number drawn at the lottery is of course 3 in 10, or 3/10, because we have
3 picks and there are 10 possible values for the first number. Assuming success
with the first draw, then our chances for matching the second number drawn are
2/9, because we have 2 numbers left and the second number is drawn from the
9 remaining. The chance of matching the third number drawn is 1/8. In total,
our chances of matching all three numbers are (3/10) * (2/9) * (1/8), which
we reformat as (1*2*3) / (8*9*10). A pattern appears: The odds of picking m
numbers out of the range 1..n are (1*2*...*m ) / ((n-m)+1 * (n-m)+2 *...*n)
which is succinctly coded as the formula presented above. End Footnote.)
4. Write an application that can translate between Arabic numbers (ordinary non-
negative integers) and Roman numerals. (Recall that Roman numeral I denotes
1, V denotes 5, X denotes 10, L denotes 50, and C denotes 100. The symbols of
a numeral should be in nonincreasing order, e.g. LXXVIII is 78, but an out-of-
order symbol can be used to subtract from the symbol that follows it, e.g., XCIV
is 94.)
The application must be able to translate any Arabic numeral in the range 1
to 399 into a Roman numeral and vice versa. (Recall that no letter in a roman
number can appear more than three times consecutively, e.g., you must write
IX rather than VIIII for 9.)
s == a b c d e f g
p == c d e
current position == 0
The “current position” remembers the index where the pattern is aligned. Since
the match fails, the search moves one character:
372
s == a b c d e f g
p == c d e
current position == 1
The process proceeds until a match succeeds or the pattern cannot be shifted
further.
7. (a) Design a calendar program, which takes two inputs: a month (an integer
in range 1..12) and a year (for simplicity, we require an integer of 1900
or larger) and presents as its output the calendar for the month and year
specified. We might use the program to see the calendar for December,
2010:
Su Mo Tu We Th Fr Sa
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
The application’s model will need methods to calculate how many days are
in a given month (within a given year), on what day of the week a given
month, year begins, whether a year is a leap year, etc.
(b) Next, revise the application so that, on demand, the calendar prints a
proper French calendar, that is, the days are presented Monday-Tuesday-
...-Sunday (lundi-mardi-mercredi-jeudi-vendredi-dimanche) with the labels,
lu ma me je ve sa di
8. Write a program that implements the word game, “hangman.” Two people
play: The first inserts a “secret word” into the game, and the second tries to
guess the secret word, one letter at a time, before six wrong guesses are made.
After the first player has typed the secret word (e.g., apple), the second sees
the following on the console:
Pick a letter:
7.14. PROGRAMMING PROJECTS 373
The second player guesses a letter, e.g., e. The program replies whether or not
the guess was correct:
Pick a letter:e
Correct!
__
| |
|
|
|
_ _ _ _ e
The program displays the partially guessed word and a “hangman’s pole” upon
which the wrong guesses, drawn as a stick figure, are compiled. The game
continues with the second player making guesses, one by one, until either the
word is guessed correctly, or the six-piece stick figure is displayed:
Pick a letter:x
__
| |
| 0
| /|\
| /\
_ p p _ e
You lose--the word was "apple"!
(Note: this program can be written with judicious use of the charAt and
substring methods for strings; see Table 9, Chapter 3, for details about these
methods.)
9. Euclid’s algorithm for calculating the greatest common divisor of two nonneg-
ative integers is based on these laws:
• GCD(x,y) = x, if x equals y
• GCD(x,y) = GCD(x-y,y), if x > y
• GCD(x,y) = GCD(x,y-x), if y > x
10. Newton’s approximation method for square roots goes as follows: The square
root of a double, n, can be approximated as the limit of the sequence of values,
ni , where
374
• n0 = n
• ni+1 = ((n/ni ) + ni )/2
Write a method that takes as its parameters two doubles: the number n and
a precision, epsilon. The method prints the values of ni until the condition
|nk+1 − nk | < epsilon holds for some k > 0. The result of the method is nk+1 .
(Hint: the Java operation Math.abs(E) computes |E|, the absolute value of E.)
Insert this method into a test program and try it with various values of epsilon.
11. Recall yet again this formula for the monthly payment on a loan of principal,
p (double), at annual interest rate, i (double), for a loan of duration of y (int)
years:
(1 + i)y ∗ p ∗ i
annual payment =
(1 + i)y − 1
monthly payment = annual payment/12.0
The monthly payment history on a loan of principal, p, at annual interest rate,
i, is computed as follows:
• p0 = p
• pj+1 = ((1 + (i/12))pj ) − monthly payment
for j >= 0.
Use these formulas to write an application that that calculates the monthly
payment for a loan and prints the corresponding payment history. Test your
application on various inputs, and explain why the principal is completely repaid
a bit sooner than exactly y full years.
12. Write an output-view class that contains these two methods for formatting
numbers. The first is
The second method is also called format and uses the following interface:
7.14. PROGRAMMING PROJECTS 375
Insert the two methods in a class called Formatter and use the class to print
nicely formatted output, e.g.,
13. Write a program that lets two players play a game of tic-tac-toe (noughts and
crosses). (Hint: the model simulates the the game board, whose internal state
can be represented by three strings, representing the three rows of the board.
Write methods that can insert a move into the board and check for three-in-
a-row. (Note: use the charAt and substring methods for strings; see Table 9,
Chapter 3 for details about substring.)
It is simplest to build the game for only one player at first; then add a second
player. Next, revise the game so that the computer can compete against a
human player.
14. Program an animation of a falling egg. The animation will show, at correct
376
velocity, an egg falling from a great height and hitting the ground.
height = I0 − (0.5 ∗ g ∗ t2 )
where I0 is the egg’s initial height (in meters), t is the time (in seconds) that
the egg has been falling, and g is the acceleration due to gravity, which is
9.81meters/second2 .
7.14. PROGRAMMING PROJECTS 377
The animation should let its user specify the initial height from which the egg
is dropped. When the egg reaches height 0, it must “smash” into the ground.
15. Program an animation of a cannon ball shot into the air: The position, x,y,
of the cannon ball in the air after t seconds have elapsed is defined by these
formulas:
x = initial velocity ∗ cosine(radians angle) ∗ t
y = (initial velocity ∗ sine(radians angle) ∗ t) − ((gravity ∗ t2 )/2)
where initial velocity is the velocity of the ball when it first leaves the cannon,
gravity is the pull of gravity, and radians angle is computed as radians angle =
(degrees ∗ P I)/180 degrees is the angle that the cannon was pointed when it
shot the cannon ball.
Your application should let the user experiment with different initial velocities,
degrees angles, and gravitational constants.
16. Make the cannon-ball animation into a game by drawing a bucket (pail) at a
random spot near the end of the graphics window and letting the user adjust
the velocity and angle of the cannon to try to make her cannon ball fall into
the bucket.
17. Program an animation of a clock—every second, the clock redisplays the time.
(To start, program a digital clock. Next, program a clock with a face and three
moving hands; see Chapter 4 for advice at drawing clock hands.)
|
| v |
| |
-----+ +----
->
-----+ +----
| |
| |
For simplicity, assume that traffic on one street travels only from north to south
and traffic on the other street travels only from west to east. The simulation
maintains at most one car at a time travelling from north to south and at most
one car at a time travelling from east to west. When one car disappears from
view, another car, travelling in the same direction, may appear at the other
end of the street. All cars travel at the same velocity of 10 meters (pixels) per
second.
378
Assume that both roadway are 90 meters in length and that the intersection
has size 10-by-10 meters.
Build the simulation in stages:
(a) First, generate north-south cars only: A new north-south car appears
within a random period of 0-2 seconds after the existing north-south car
disappears from view. Next, add a counter object that counts how many
cars complete their journey across the intersection in one minute.
(b) Next, generate east-west cars: A new east-west car appears within a ran-
dom period of 0-5 seconds after the existing one disappears. Count these
cars as well. (Do not worry about collisions at the intersection.)
(c) Now, program the cars so that a car must stop 20 meters (pixels) before
the intersection if a car travelling in another direction is 20 meters or
closer to entering the intersection. (If both cars are equi-distant from the
intersection, the east-west car—the car on the “right”—has right-of-way.)
Count the total flow of cars in one minute.
(d) Experiment with stop signs at the intersection: A stop sign causes a car to
stop for 1 second, and if another car is within 20 meters of the intersection
at the end of the 1 second, the stopped car must wait until the moving car
crosses the intersection. Place a stop sign on just the east-west street and
count the total traffic flow for one minute. Do the same for a stop sign
just on the north-south street.
(e) Finish the simulation with two stop signs; count cars.
These optional sections expand upon the materials in the chapter. In particular,
correctness properties of loops and recursively defined methods are studied in depth.
7.15. BEYOND THE BASICS 379
A loop that does indefinite iteration must terminate at some point, and often the
decision to terminate is made in the middle of the loop’s body. Java provides a
statement named break that makes a loop quit at the point where the termination
decision is made.
For example, Figure 5 showed how the findChar method used indefinite iteration
to search for the leftmost occurrence of a character in a string. Once the character
is located, a boolean variable, found, is set to true, causing the loop to quit at the
beginning of the next iteration. We can terminate the loop immediately by using a
break statement instead of the assignment, found = true:
public int findChar(char c, String s)
{ int index = 0; // where to look within s for c
while ( index < s.length() )
// invariant: at this program point,
// c is not any of chars 0..(index-1) in s
{ if ( s.charAt(index) == c )
{ break; } // exit loop immediately
else { index = index + 1; }
}
// If the break executed, this means index < s.length() is still true
// at this point and that s.charAt(index) == c.
if ( !(index < s.length()) ) // did the loop terminate normally?
{ index = -1; } // then c is not found in s
return index;
}
The break statement causes the flow of control to move immediately from the middle
of the loop to the first statement that follows the loop. For this reason, the found
variable no longer controls loop termination. This simplifies the loop’s test but forces
us to write a more complex conditional that follows the loop.
When a break statement is inserted into a loop, it means that the loop has more
than one way of exiting—it can exit by failure of its test and by execution of the
break. For this reason, a break statement should be used sparingly and with caution:
insert a break only in a position where the reason for loop exit is perfectly clear
In the above example, the break occurs exactly when s.charAt(index) == c, which
is a crucial property for describing what the loop does. This property is crucial to
the the conditional statement that follows the loop. If you are not completely certain
about inserting a break statement into a loop, then don’t do it.
Finally, note that a break exits only one loop’s body. For example, the following
nested loop appears to make the values of variables i and j vary from 0 to 3. But
the break statement in the body of the inner loop causes that loop to terminate
prematurely:
380
int i = 0;
while ( i != 4 )
{ int j = 0;
while ( j != 4 )
{ if ( i + j == 3 )
{ break; }
j = j + 1;
}
System.out.println("i = " + i + ", j = " + j);
i = i + 1;
}
The loops print
i = 0, j = 3
i = 1, j = 2
i = 2, j = 1
i = 3, j = 0
The above example should make clear that a break statement can make a program
more difficult to understand.
Answer the question: Say that the loop has been iterating for some time; what has it
accomplished so far? An informal but accurate answer is, “the loop has been printing
squares.” Indeed, this is an invariant, and it is useful to document the loop exactly
this way:
while ( i != (n + 1) )
// the loop has been printing squares
{ System.out.println(i * i);
i = i + 1;
}
Someone who is more mathematically inclined might formulate a more precise answer:
The loop has been printing the squares 0*0, 1*1, 2*2, etc. Indeed, the value of its
counter variable, i, states precisely how many squares have been printed:
382
while ( i != (n + 1) )
// the loop has printed the squares 0*0, 1*1, ...up to... (i-1)*(i-1)
{ System.out.println(i * i);
i = i + 1;
}
This is also an invariant, because whether the loop has iterated 2 times, 20 times,
etc., the invariant states exactly what has happened. For example, after 20 iterations,
i has value 21, and indeed the loop has printed from 0*0 up to 20*20. (If the loop
has iterated zero times, i has value 0 and the invariant says the loop has printed from
0*0 up to -1*-1, that is, it has printed nothing so far.)
When the loop terminates, its test, i != (n + 1), evaluates to false. Hence, i has
the value, n+1. But we know that the invariant is still true, and by substituting n+1
for i, we discover that the loop has printed exactly the squares from 0*0 up to n*n.
This exposes the ultimate goal of the loop.
Next, reconsider the summation example and its proposed invariant:
1. basis step: we show the invariant is true the very first time the loop is encoun-
tered.
2. inductive step: We assume the invariant is already holding true at the start of an
arbitrary iteration of the loop, and we show that when the iteration completes
the invariant is still true.
Such a proof style is known as proof by induction on the number of iterations of the
loop.
The proof of the invariant for the summation loop is an induction:
1. When the loop is first encountered, both total and i have value 0, therefore it
is true that total == 0 + ...up to... + 0.
and we show that the statements in the loop’s body update the invariant so
that it holds true again at the end of the iteration:
We use V start to represent the value of variable V at the start of the iteration
and V next to represent the new value V receives during the iteration.
Here is the proof:
We conclude, no matter how long the loop iterates, the invariant remains true.
The previous examples were of definite iteration loops, where the pattern of in-
variant takes the form
In the case of input processing, the natural invariant for the transaction-processing
loop is simply,
Searching loops often have invariants in two parts, because the loops can exit in
two different ways; the general format is
384
Clauses 1 and 2 are necessary because the value of item found can possibly change
from false to true within an iteration.
If you write a loop and you “know” what the loop does, but you find it difficult
to state its invariant, then you should generate execution traces that exhibit values
of the loop’s variables and examine the relationships between the variable’s values at
the various iterations. These relationships often suggest an invariant. For example,
say that we believe this loop computes multiplication of two nonnegative integers a
and b via repeated additions:
int i = 0;
int answer = 0;
while ( i != a )
{ answer = answer + b;
i = i + 1;
}
The question is: Say that the loop has been iterating for some time; what has it
accomplished so far? We can use the debugging tool of an IDE to print an execution
trace, or we might make the loop generate its own trace with a println statement:
int i = 0;
int answer = 0;
while ( i != a )
{ System.out.println("i=" + i + " a=" + a + " b=" + b
+ " c=" + c + " answer=" + answer);
answer = answer + b;
i = i + 1;
}
When a is initialized to 3 and b is initialized to 2, we get this trace:
i=0 a=3 b=2 answer=0
i=1 a=3 b=2 answer=2
i=2 a=3 b=2 answer=4
i=3 a=3 b=2 answer=6
7.15. BEYOND THE BASICS 385
We see that i * b = answer holds at the start of each iteration. This is indeed the
crucial property of the loop, and when the loop terminates, we conclude that i = a
and therefore a * b = answer.
These examples hint that one can use loop invariants and elementary symbolic
logic to construct formal proofs of correctness of computer programs. This is indeed
the case and unfortunately this topic goes well beyond the scope of this text, but a
practical consequence of invariants does not: A programmer who truly understands
her program will be capable of stating invariants of the loops it contains. As a matter of
policy, the loops displayed in this text will be accompanied by invariants. Sometimes
the invariant will be stated in mathematical symbols, and often it will be an English
description, but in either case it will state the property that remains true as the
iterations of the loop work towards the loop’s goal.
Exercises
State invariants for these loops and argue as best you can that the invariants remain
true each time the loop does an additional iteration:
1. Multiplication:
2. Division:
3. Exponentiation:
386
String s = ... ;
boolean item_found = false;
int index = 0;
while ( !item_found && (index < s.length()) )
{ if ( s.charAt(index) == ’A’ )
{ item_found = true; }
else { index = index + 1; }
}
5. String reverse:
String s = ...;
String t = "";
int i = 0;
while ( i != s.length() )
{ t = s.charAt(i) + t;
i = i + 1;
}
The termination expression is a - i, because each time the loop completes an iter-
ation, the value of a - i decreases. Since a is a nonnegative number, at some point
i’s value will equal a’s, and the termination expression’s value reaches 0, and at this
very moment, the loop does indeed terminate. (Again, we require that a and b are
nonnegative integers.)
Definite iteration loops use termination expressions that compare the value of the
loop counter to a stopping value, because the increment of the loop counter within
the loop’s body makes the counter move closer to the stopping value.
Searching loops ensure termination by limiting their search to a finite collection.
In the case of the example in Figure 4, where the loop searches for divisors, the termi-
nation expression is current - 1, because variable current is searching for possible
divisors by counting from n/2 down to 1. When the termination expression reaches
0, it forces the loop’s test to go false.
Unfortunately, not all loops have such termination expressions; a loop that imple-
ments a divergent infinite series clearly lacks such an “alarm clock.” An even simpler
example is a loop that reads a sequence of transactions submitted by a user at the
keyboard—there is no termination expression because there is no guarantee that the
user will tire and terminate the sequence of inputs.
Exercises
For each of the loops in the previous exercise set, state the conditions under which
each is guaranteed to terminate and explain why.
• Remove the header line from the constructor method and replace it by public
void init().
• Remove the enclosing frame and all invocations of setSize, setTitle, and
setVisible.
If the constructor method uses parameters, then you are out of luck. (But
Chapter 10 presents an awkward way of binding strings to variables within
init.)
We must work harder to make the animation example into an applet—the start-up
and controller classes must be squeezed into the output-view class, AnimationWriter.
The unfortunate result appears in Figure 22. The controller must be moved into the
applet’s paint method, because a newly created applet first executes its init method,
followed by paint. The “real” painting method, that of the output view, is renamed
paintAnimation and is invoked from within paint.
In Chapter 10, we learn how to avoid such surgeries.
Chapter 8
8.6.1 Behaviors
8.6.2 Architecture
8.6.3 Specifications
8.6.4 Implementation
8.12 Summary
Computer programs often manage many objects of the same type, e.g., a bank’s
accounting program must manage hundreds of customer accounts. It is inconvenient
and usually impossible to declare distinctly named variables for each of the customer
accounts; instead, one constructs a new form of object—a data structure—to collec-
tively hold and name the customer accounts.
The most popular form of data structure is the array, and this chapter introduces
standard uses of arrays. After studying this chapter, the reader should be able to
392
and say that you must write a method that locates and prints the highest score of
the six. How will you do this? Alas, the method you write almost certainly will use
a sequence of conditional statements, like this:
double high_score = score0;
if ( score1 > high_score ) { high_score = score1; }
if ( score2 > high_score ) { high_score = score2; }
if ( score3 > high_score ) { high_score = score3; }
if ( score4 > high_score ) { high_score = score4; }
if ( score5 > high_score ) { high_score = score5; }
System.out.println(high_score);
This unpleasant approach becomes even more unpleasant if there are more even scores
to compare or if a more difficult task, such as ordering the scores from highest to
lowest, must be performed. Some other approach is required.
Can we employ a loop to examine each of score0 through score6? But the six
variables have distinct names, and a loop has no way of changing from name to name.
Ideally, we wish to use “subscripts” or “indexes,” so that we may refer to score0 as
score0 , score1 as score1 , and so on. The notation would be exploited by this for-loop,
The name of this variable is score, and its declared data type is double[] (read this
as “double array”). The data type indicates that score is the name of a collection
of doubles, and the doubles named by the array variable are score[0], score[1], ...,
score[5].
The initialization statement’s right-hand side, new double[6], constructs a new
form of object that holds six elements, each of which is a double variable.
It is traditional to draw an array like this:
0 1 2 3 4 5
score 0.0 0.0 0.0 0.0 0.0 0.0
The diagram shows that a newly constructed array that holds numbers starts with
zeros in all the elements.
When we wish to refer to one of the elements in the array named score, we use
an index (also known as subscript) to identify the element. The elements are indexed
as score[0], score[1], and so on, up to score[5]. For example, we can print the
number held in element 3 of score by writing,
System.out.println(score[3]);
Java requires that an array’s indexes must be integers starting with zero.
It is helpful to think of variable score as the name of a “hotel” that has six
“rooms,” where the rooms are labelled 0 through 5. By stating score[3], we specify
the precise address of one of the hotel’s rooms, where we locate the room’s “occupant.”
Of course, each of the elements of an array is itself a variable that can be assigned.
For example, say that the leading judge gives the score, 5.9, to a skater. We insert
the number into the array with this assignment:
score[0] = 5.9;
score[1] = 4.4;
394
We can insert values into all the array’s elements in this fashion.
But most importantly, the index contained within the square brackets may be a
variable or even an integer-valued arithmetic expression. For example,
int i = 3;
System.out.println(score[i]);
System.out.println(score[i + 1]);
locates and prints the doubles held in elements 3 and 4 of score. By using variables
and expressions as indexes, we can write intelligent loops, such the one that locates
and prints a skater’s highest score:
double high_score = score[0];
for ( int i = 1; i <= 5; i = i + 1 )
// invariant: high_score holds the highest score in the range,
// score[0] ..upto.. score[i-1]
{ if ( score[i] > high_score )
{ high_score = score[i]; }
}
System.out.println(high_score);
By changing the value of i at each iteration, the loop’s body examines all the array’s
elements, solving the problem we faced at the beginning of this section.
As noted above, we can use integer-valued arithmetic expressions as indexes. For
example, perhaps variable i remembers an array index; if we wish to exchange the
number in score[i] with that of its predecessor element, we can write
int temp = score[i];
score[i - 1] = score[i];
score[i] = temp;
The phrase, score[i - 1], refers to the element that immediately precedes element
score[i]. For example, for the array pictured earlier and when i holds 2, the result
of executing the above assignments produces this array:
0 1 2 3 4 5
score 5.9 0.0 4.4 0.0 0.0 0.0
The above example showed how an array might hold a set of numbers. But arrays
can hold characters, booleans, strings, and indeed, any form of object whatsoever. For
example, the words of a sentence might be stored into an array like this:
String[] word = new String[3];
word[0] = "Hello";
word[1] = "to";
word{2] = "you";
The previous sequence of statements constructs two BankAccount objects and assigns
them to array r.
Because they can hold numbers, booleans, characters, and objects, arrays are
heavily used in computer programming to model sets or collections. The collection of
skating scores seen at the beginning of this section is one simple example, but there
are many others:
• a library’s books
• indeed, any “data bank,” where multiple objects are held for reference
The sections that follow show how to use arrays to model these and other examples.
Exercises
1. Say that we declare this array:
What do each of these loops print? (Hint: It will be helpful to draw a picture
of array r, like the ones seen in this section, and update the picture while you
trace the execution of each loop.)
396
2. Declare an array, powers of two, that holds 10 integers; write a for-loop that
places into powers of two[i] the value of 2i , for all values of i in the range, 0
to 9.
3. Declare an array, letter, that holds 26 characters. Write a for-loop that ini-
tializes the array with the characters, ’a’ through ’z’. Next, write a loop that
reads the contents of letter and prints it, that is, the letters in the alphabet,
all on one line, in reverse order.
3
3
0
8.2. COLLECTING INPUT DATA IN ARRAYS 397
and so on.
Vote counting will go smoothly with an array that holds the tallies for the four
candidates: We construct an array whose elements are integers,
int[] votes = new int[4];
where votes[0] holds Candidate 0’s votes, and so on. When a vote arrives, it must
be added to the appropriate element:
int v = ...read the next vote from the input...
votes[v] = votes[v] + 1;
The algorithm for vote counting follows the “input processing pattern” from Chapter
7:
boolean processing = true;
while ( processing )
{ int v = ...read the next vote from the input...
if ( v is a legal vote, that is, in the range, 0..3 )
{ votes[v] = votes[v] + 1; }
else { processing = false; }
}
Once all the votes are tallied, they must be printed. There is a standard way of
printing the contents of an array like votes:
for ( int i = 0; i != votes.length; i = i + 1 )
// invariant: values of votes[0]..votes[i-1] have been printed
{ System.out.println( ... votes[i] ... ); }
A for-loop works with a loop-counter variable, i, to print all the array’s elements.
Notice the phrase, votes.length, which is new and appears in the loop’s termination
test: The phrase is a built-in Java convenience that denotes the length of array votes
(here, 4). It is always better to use votes.length, rather than 4, in the coding, because
the former need not be changed if array votes is changed in a later version of the
program.
Figure 1 presents the resulting vote counting program. For example, if the elec-
tion’s votes arrived in the order, 3, 3, 0, 2, 3, followed by a terminating number,
e.g., -1, the program prints
398
The conditional adds the vote to the array only if the vote falls within the range
0..3. If an improperly valued v, say, 7, was used with votes[v] = votes[v] + 1, then
execution would halt with this exception,
java.lang.ArrayIndexOutOfBoundsException: 7
at Test.main(VoteCount.java:...)
Exercises
1. Modify class VoteCount in Figure 1 so that it prints the total number of votes
cast and the winning candidate’s “name.”
2. Modify class VoteCount so that the application first asks for the number of
candidates in the election. After the number is typed, then votes are cast as
usual and the results are printed.
3. Modify class VoteCount so that the application first requests the names of the
candidates. After the names are typed, the votes are cast as as usual and the
results are printed with each candidate’s name and votes. (Hint: Use an array
of type String[] to hold the names.)
4. Write an application that reads a series of integers in the range 1 to 20. The
input is terminated by an integer that does not fall in this range. For its output,
the application prints the integer(s) that appeared most often in the input, the
integer(s) that appeared least often, and the average of all the inputs.
This simple substitution code is all too easy for outsiders to decipher.
Here is a slightly more challenging substitution code: Starting with a “seed”
integer, k, we encode a blank space by k. Call this value code(’ ’). Next, we encode
the alphabet in this pattern, where each character’s code is the twice as large as its
precedessor’s, plus one:
code(’ ’) = k;
code(’a’) = (code(’ ’) * 2) + 1
code(’b’) = (code(’a’) * 2) + 1
...
code(’z’) = (code(’y’) * 2) + 1
Exercises
1. Write the complete application which takes a seed integer and a line of words
as input and produces a series of integer codes as output.
2. Write the corresponding decoder program, which takes the seed integer as its
first input and then reads a sequence of integers, which are decoded into char-
acters and printed.
3. Write statements that construct a translation table, powers of two, and assign
to the table the powers of two, namely,
powers_of_two[i] = 2^i
summation(0) = 0
summation(n) = n + summation(n-1), if n > 0
Write applications that build tables (arrays of 20 elements) for the following
recursive definitions; make the applications print the contents of each table, in
reverse order.
(a) The factorial function:
0! = 1
n! = n * (n-1)!, when n is positive
(b) The Fibonacci function:
Fib(0) = 1
Fib(1) = 1
Fib(n) = Fib(n-1) + Fib(n-2), when n >= 2
This example is especially interesting, because it is far more efficient to
compute the entire translation table for a range of Fibonnaci values and
consult the table just once, than it is to compute a single Fibonnaci value
with a recursively defined method. Why is this so?
402
This declares variable r with data type int[] (“int array”). But variable r is meant
to hold the address of an array object; it is not itself the array. We use assignment to
place an address in r’s cell:
r = new int[6];
As noted earlier, the phrase, new int[6], constructs an array object of six integer
elements. We can do the two previous two steps in a single initialization statement:
int[] r = new int[6];
The address, a1, of the array object, new int[6], is saved in variable r. Notice that
the run-time data type of the object at address a1 is int[6]—the data type includes
the arrays’s length and its elements’ data type.
Many programmers fail to understand the difference between an array variable
and an array object and try to duplicate an array like this:
int[] s = r;
This statement duplicates the address of the array object and not the object itself!
When we examine computer storage, we see that variable s holds the same address
held by r—the two variables share the same array object:
a1 : int[6]
int[ ] r == a1 0 1 2 3 4 5
0 0 0 0 0 0
int[ ] s == a1
This means assignments to s’s elements alter r’s elements as well: For example, s[0]
= 3 will make r[0] == 3 as well.
8.4. INTERNAL STRUCTURE OF ONE-DIMENSIONAL ARRAYS 403
An array can be partially filled with objects, just like an hotel can have some occu-
pied rooms and some vacancies. We can test if an element is occupied by comparing
the element’s value to null. For example, here is a loop that prints the balances of
all accounts in array account, skipping over those elements that are empty:
for ( int i = 0; i != account.length; i = i + 1 )
{ if ( account[i] != null )
{ System.out.println( "Balance of account " + i
+ " is " + account[i].balanceOf() );
}
}
404
Because they are objects, array objects can be parameters to methods and can be
results from methods. Here is an example: Method reverse accepts (the address of)
an array object as its argument and returns as its result a newly constructed array
object whose elements are arranged in reverse order of those in its argument:
public double[] reverse(double[] r)
{ double[] answer = new double[r.length];
for ( int i = 0; i != r.length; i = i+1 )
{ answer[(r.length - 1) - i] = r[i]; }
return answer;
}
it is the address of the four-element array named by d that is bound to the formal
parameter, r. Inside the method, a second array is constructed, and the numbers held
in the array named d are copied into the new array object. When the method finishes,
it returns the address of the second array—variables d and e hold the addresses of
distinct array objects.
The main method that comes with every Java application uses an array parameter,
args:
Exercises
Create the following arrays and assign to their elements as directed.
1. An array, r, of 15 integers, such that r[0] = 0, and the value of all the other
r[i]s, i in 1..14, is the summation of i. (Hint: Use the algorithm underlying
Figure 1, Chapter 7, to calculate summations.)
2. An array, d, of 30 doubles, such that value of each d[i] is the square root of i.
(Hint: Use Math.sqrt.) For i ranging from 0 to 29, print i and its square root.
3. An array, b, of 4 booleans, such that the value of b[0] is true, the value of b[1]
is the negation of b[0], and the value of b[2] is the conjunction of the previous
two elements. (The value of b[3] does not matter.) Print the values of b[3]
through b[1].
(a) Write a for-loop that creates one hundred distinct bank accounts, each
with starting balance of 0, and assigns the accounts to the elements of
bank.
(b) Write a statement that adds 50 to the account at element 12; write state-
ments that transfer all the money in the account at element 12 to the
account at element 45.
(c) Write a for-loop that prints the index numbers and balances for all accounts
that have nonzero balances.
(d) Write an assignment statement that makes the account at element 12 van-
ish.
(e) Explain what happens when this assignment is executed: bank[15] = bank[10];
This constructs an array that has 100 elements, such that each element holds the
initial value, null. (There are no accounts yet, just a structure to hold the accounts.)
Now, say that a customer opens an account at the bank with an initial desposit of
200, and the customer wants her account to have the identification number, 75. The
programming statements that enact this request might read,
BankAccount new_account = new BankAccount(200);
bank[75] = new_account;
8.5. ARRAYS OF OBJECTS 407
Here is a diagram of the array and the newly constructed object whose address is
saved within the array:
a1 : BankAccount[100]
BankAccount[] bank == a1 0 1 . . . 75 . . . 99
null null . . . a2 . . . null
a2 : BankAccount
int balance == 20
. . .
will preform the action. Note that bank[75] names the bank account object whose
withdraw method is invoked. This example emphasizes that the array, bank, holds
objects that are indexed by integers.
Next, say that the customer closes her account, so that it is no longer needed by
the bank. The bank “erases” the account by stating,
bank[75] = null;
Since null means “no value,” this means another customer might open an account
and choose 75 for its identification number.
As just noted, an array can be partially filled with objects; we can test if an array
element is occupied by comparing the element’s value to null. For example, here
is a loop that prints the balances of all accounts in array bank, skipping over those
elements that are empty:
for ( int i = 0; i != bank.length; i = i + 1 )
{ if ( bank[i] != null )
{ System.out.println( "Balance of account " + i
+ " is " + bank[i].getBalance() );
}
}
With these basic ideas in mind, we can write a simple bank-accounting application
that lets customers open bank accounts, make deposits and withdrawals, check the
balances, and close the accounts when they are no longer needed—we use an array to
hold the accounts, and we use the array’s indexes as the identification numbers for
the accounts.
408
Exercises
1. Here is a class, Counter, that can remember a count:
public Counter(int v) { c = v; }
public void increment() { c = c + 1; }
public int getCount() { return c; }
}
2. Let’s write a class that “models” the bank described in this section:
• A library’s database holds records of books. Each record has for its key the
book’s catalog number. For example, the U.S. Library of Congress catalog
number is a pair: an alphabetic string and a fractional number, such as QA
76.8. The records held in a library’s database would have these attributes:
• The U.S. Internal Revenue Service database hold records of taxpayers. Each
record is identified by a nine-digit social-security number. The record holds the
number as its key and also holds the taxpayer’s name, address, and copies of
the person’s tax reports for the past five years.
Although the example records just listed differ markedly in their contents, they share
the common feature of possessing a key. This crucial feature helps us understand the
function of a database:
A database is a container that locates records by using the records’ keys as indices.
2. records are objects, and a record holds as one of its attributes (the address of)
its key object
8.6. CASE STUDY: DATABASES 411
4. when the database’s user wishes to insert a record into the database, she calls
the databases’ insert method, supplying the record as the argument; when she
wishes to find a record, she calls the find method, supplying a key object as
an argument; when she wishes to delete a record, the calls the delete method,
supplying a key object as an argument
For example, if we build a database to hold library books, the key objects will be
Library of Congress catalog numbers, and each record object will hold (the address
of) a key object and information about a book. Such records are inserted, one by
one, into the database. When a user wishes to find a book in the database, she must
supply a key object to the database’s find method and she will receive in return (the
address of) the desired book object; an informal picture of this situation looks like
this:
a1 : Key
QA a3 : Database
76.9 insert(a2)
. . . a3 . . .
a2 : Record
a1
”Charles Dickens” a4 : Key find(a4)
”A Tale of Two Cities” QA
borrowed 76.9
...
The picture suggests that the database will operate the same, regardless of whether
books, bank accounts, and so on, are saved. As long as the records—whatever they
are—hold keys, the database can do its insertions, lookups, and deletions, by manip-
ulating the records’ keys and not the records themselves. This is strongly reminiscent
of arrays, which can hold a variety of objects without manipulating the objects them-
selves.
So, how does a database manipulate a key? Regardless of whether keys are num-
bers or strings or pairs of items, keys are manipulated by comparing them for equality.
Consider a lookup operation: The database receives a key object, and the database
searches its collection of records, asking each record to tell its key, so that each key
can be compared for equality to the desired key. When an equality is found true, the
corresponding record is returned. This algorithm operates the same whether integers,
strings, or whatever else is used for keys.
In summary,
1. The Database holds a collection of Record objects, where each Record holds a Key
412
3. Records, regardless of their internal structure, will possess a getKey method that
returns the Record’s Key object when asked.
4. Key objects, regardless of their internal structure, will have an equals method
that compares two Keys for equality and returns true or false as the answer.
We are now ready to design and build a database subassembly in Java. We
will build a subassembly—not an entire program—such that the subassembly can be
inserted as the model into a complete application. We follow the usual stages for
design and construction:
1. State the subassembly’s desired behaviors.
3. For each of the architecture’s components, specify classes with appropriate at-
tributes and methods.
8.6.1 Behaviors
Regardless of whether a database holds bank accounts, tax records, or payroll infor-
mation, its behaviors are the same: a database must be able to insert, locate, and
delete records based on the records’ keys. We plan to write a class Database so that
an application can construct a database object by stating,
Database db = new Database(...);
Then, the application might insert a record—call it r0—into db with a method invo-
cation like this:
db.insert(r0);
As stated earlier, each record possesses its own key. Say that record r0 holds object
k0 as its key. To retrieve record r0 from the database, we use a command like this:
Record r = db.find(k0);
This places the address of record r0 into variable r for later use. We can delete the
record from the database by stating:
8.6. CASE STUDY: DATABASES 413
Database 1 *
insert Record
find getKey(): Key
delete
Key
equals(Key y): boolean
db.delete(k0);
Notice that variable r still holds the address of the record, but the record no longer
lives in the database.
The above behaviors imply nothing about the techniques that the database uses
to store and retrieve records; these activities are internal to class Database and are
best left unknown to the database’s users.
8.6.2 Architecture
The previous examples suggest there are at least three components to the database’s
design: the Database itself, the Records that are inserted into it, and the Keys that
are kept within records and are used to do insertions, lookups, and deletions. The
class diagram in Figure 2 lists these components and their dependencies. There
is a new notation in the Figure’s class diagram: The annotation, 1 --> *, on the
arrow emphasizes that one Database collaborates with (or collects) multiple Records,
suggesting that an array will be useful in the coding of class Database. As noted
earlier, whatever a Record or Key might be, the methods getKey and equals are
required. (The format of the equals method will be explained momentarily.)
8.6.3 Specifications
To keep its design as general as possible, we will not commit class Database to saving
any particular form of Record—the only requirement that a database will make of a
record is that a record can be asked for its key. Similarly, the only requirement a
database will make of a key is that the key can be compared to another key for an
equality check.
Since class Database must hold multiple records, its primary attribute will be
an array of records, and the database will have at least the three methods listed in
Figure 2.
414
must write K1.equals(K2) to ask if the two keys have the same value. (This is similar
to writing S1.equals(s2) when comparing two strings, S1 and S2, for equality.) We
exploit this generality in the next section.
8.6.4 Implementation
The specifications for Record and Key make it possible to write a complete coding for
class Database without knowing any details about the codings for the records and
keys. Let’s consider the implementation of class Database.
The database’s primary attribute is an array that will hold the inserted records.
class Database must contain this field declaration:
The constructor method for the class will initialize the field to an array:
base = new Record[HOW_MANY_RECORDS];
where all the array’s elements have value null, because the array is empty. Records
will be inserted into the database one by one. To do an insert(Record r), follow this
algorithm:
1. Search array base to see if r is present. (More precisely, search base to see if a
record with the same key as r’s key is already present.)
2. If r is not in base, then search for the first element in base that is empty (that
is, holds value null).
because r.keyOf() extracts the key held within record r, and a result of -1 from
findLocation means that no record with the same key is already present.
Step 2 of the algorithm is clearly a searching loop, and we use the techniques from
Chapter 7 to write this loop, which searches for the first empty element in base where
a new record can be inserted:
416
When this loop completes, i holds the index of the first empty element in base,
meaning that Step 3 is just base[i] = r, unless array base is completely filled with
records and there is no available space. What should we do in the latter situation?
Because Java arrays are objects, it is possible to construct a new array object that
is larger than the current array and copy all the elements from the current array to
the new array. Here is a standard technique for doing so:
The last assignment, base = temp, copies the address of the larger array into array
variable base, meaning that base once again holds the address of an array of records.
BeginFootnote: If you have studied the Java libraries, perhaps you discovered
class Vector, which behaves like an array but automatically expands to a greater
length when full. The technique that a Java Vector uses to expand is exactly the one
presented above. EndFootnote.
Figure 4 displays the completed version of insert.
Next, we consider how to delete an element from the database: The algorithm for
method, delete(Key k), would go,
We use the helper method, findLocation, to code Step 1. We have this coding:
The test expression first asks if there is a record stored in element, base[i], and if
the answer is true, then the element’s key (namely, base[i].keyOf()) is compared for
equality to the desired key, k.
The completed Database class appears in Figure 4. In addition to attribute base,
we define the variable, NOT FOUND, as a memorable name for the -1 answer used to
denote when a search for a record failed.
The coding presents several lessons:
• Although class Database appears to store records based on their keys, a more
primitive structure, an array, is used inside the class to hold the records. The
helper method, findLocation, does the hard work of using records’ keys as if
there were “indices.”
• Aside from the getKey and equals methods, nothing is known about the records
and keys saved in the database. This makes class Database usable in a variety
of applications, we see momentarily.
418
• Because the array of records can be filled, we use a standard technique within
the insert method to build a new, larger array when needed.
When a customer opens a new account, we might ask the customer to select an integer
key for the account and make an initial deposit:
The fourth statement both constructs the new account and inserts it into the database.
Later, if the account must be fetched so that its balance can be checked, we can
find it and print its balance like this:
/** getInt returns the integer value held within this key */
public int getInt() { return k; }
}
422
/** equals returns whether the catalog number held within this object
* is identical to the catalog number held within c
* @param c - the other catalog number
* @return true, if this catalog number equals c; return false, otherwise */
public boolean equals(Key c)
{ String s = c.getLetterCode();
double d = c.getNumberCode();
return ( s.equals(letter code) && d == number code );
}
/** getLetterCode returns the letter code part of this catalog number
* @return the letter code, e.g., "QA" */
public String getLetterCode() { return letter code; }
/** getNumberCode returns the number code part of this catalog number
* @return the number code, e.g., "76.884" */
public double getNumberCode() { return number code; }
}
424
The structure of the catalog number is more complex: Its class Key holds a string
and a double, because we are using the U.S. Library of Congress coding for catalog
numbers, which requires a string and a fractional number. The class’s equals method
compares the strings and fractional numbers of two keys.
Here is a short code fragment that constructs a database for a library and inserts
a book into it:
Database library = new Database(50000);
Exercise
Write an application that uses class Database and classes Record and Key in Figure
5 to help users construct new bank accounts and do deposits on them.
if you have no interest in building or playing card games, modelling playing cards is
useful, because it shows how to model a set of pieces that must behave similarly.
Deck 1 * Card
newCard(): Card suit
count
Specification
The specification for a card deck is presented in Table 7. Because it is a container for
cards, class Deck requires an attribute that is an array. The class’s methods include
one that returns a card and one that replies whether or not there are more cards to
return.
CardDeck collaborates with class Card, which we consider next. As noted earlier,
a playing card has two crucial attributes: its suit and its count, as seen in Table 8.
Of course, class Card will be coded to have accessor methods that return a card’s
suit and count.
Implementation
There is a technical issue that we should resolve before we write the two classes:
When people use playing cards, they perfer to use the names of suits and counts,
426
like “hearts,” “clubs,” “ace,” and “queen.” These are values, just like integers, and
we would like to use such values when we construct the playing cards, e.g., “new
Card(queen, hearts).”
We define values like “queen” and “hearts” in Java by declaring a public static
final variable for each such value, and place the public static final variables within
class Card. We see this in Figure 9, which presents class Card.
The public static final variables declared in class Card are public names that
can be used by the other components of an application; the names are used as if they
are new values. For example, other components can refer to the values, Card.ACE,
Card.DIAMOND, and so on. Now, we can say:
public static final int SIZE OF ONE SUIT = 13; // how many cards in one suit
But remember that the public static final variables are merely names for integers
and strings. For example, since Card.QUEEN is the name of an integer, we can state,
Array deck is constructed to hold the four suits’s worth of cards, where the quantity
of cards in a suit is the static variable defined in class Card.
The class’s constructor method must fill array deck with a complete collection of
cards. As Figure 8 shows, a helper method, createSuit knows how to generate one
complete suit of cards and insert it into the array; therefore, the constructor can be
written as
createSuit(Card.SPADES);
createSuit(Card.HEARTS);
createSuit(Card.CLUBS);
createSuit(Card.DIAMONDS);
The helper method is written so that it inserts the cards into the array in ascending
order. This is not the ideal state for the deck for playing a typical card game—the
deck’s cards are expected to be randomly mixed, or “shuffled.”
Rather than write a method that randomly mixes the elements in the array, we
can write the newCard method so that when it is asked to remove a card from the
array it randomly calculates an array index from which the card is extracted. The
algorithm might go like this:
8.7. CASE STUDY: PLAYING PIECES FOR CARD GAMES 429
3. Fill the empty element at index by shifting leftwards one element the cards in
the range, deck[index + 1] up to deck[card count - 1].
Step 1 can be done with the built-in Java method, Math.random(), which computes a
nonnegative pseudo-random fraction less than 1.0:
Exercises
1. Write a test application that creates a new card, the queen of hearts, and then
asks the card what its suit and count are. The program prints the answers it
receives in the command window. Does your program print Queen or 12? What
solution do you propose so that the former is printed?
2. Write an application that creates a new deck of cards and asks the deck to deal
53 cards. (This is one more than the deck holds!) As the deck returns cards
one by one, print in the command window the count and suit of each card.
3. Write an application that lets a user request cards one by one, until the user
says, “stop.”
4. Card decks are used with card games. A typical card game has a dealer and
several players. A dealer owns a card deck and gives cards from the deck to
the players. The following specifications summarize the behavior of dealer and
player:
430
/** Constructor CardDeck creates a new card deck with all its cards */
public CardDeck()
{ createSuit(Card.SPADES);
createSuit(Card.HEARTS);
createSuit(Card.CLUBS);
createSuit(Card.DIAMONDS);
}
/** moreCards states whether the deck has more cards to give.
* @return whether the deck is nonempty */
public boolean moreCards()
{ return (card count > 0); }
Write classes for these two specifications. (Write class Player so that a player
wants cards until it has exactly five cards.) Next, write a controller that creates
a dealer object and a player object. The controller tells the dealer to deal to
the player. Then, the controller asks the player to reveal its cards.
in a matrix:
Candidate
Region 0 1 2 3
0 0 0 0 0
1 0 0 0 0
election
2 0 0 0 0
The Candidates’ names are listed along the top of the matrix (for simplicity, we call
them Candidate 0, ..., Candidate 3), labelling the columns, and the regions are listed
along the left (they are Regions 0, 1, and 2), labelling the matrix’s rows—We say that
the matrix has three rows and four columns.
Thus, a vote in Region 1 for Candidate 3 would be recorded in the middle row
within its rightmost element:
Candidate
Region 0 1 2 3
0 0 0 0 0
1 0 0 0 1
election
2 0 0 0 0
Other votes are recorded this manner. When voting is completed, we can see which
candidate received the most votes overall by adding each of the four columns.
In programming, we use a two-dimensional array to model a matrix like the one
just seen. The above matrix is coded as a two-dimensional array in Java by stating
int[][] election = new int[3][4];
The data type of variable election is int[][] (“int array array”), indicating that
individual integers in the collection named election are uniquely identified by means
of two indexes. For example,
election[1][3]
identifies the integer element that holds the votes in Region 1 for Candidate 3.
The right-hand side of the initialization, new int[3][4], constructs the array ob-
ject that holds the collection of twelve integers, organized into 3 rows of 4 elements,
each—three rows and four columns. It is helpful to visualize the collection as a ma-
trix, like the ones just displayed. As usual for Java, the array’s elements are initialized
with zeros.
Let’s do some small exercises with array election: To add one more vote from
Region 1 for Candidate 3, we write
8.8. TWO-DIMENSIONAL ARRAYS 433
election[1][3] = election[1][3] + 1;
Here, we use election[2][i] to indicate a cell within Row 2 of the matrix—the value
of i determines the specific cell in the row.
Similarly, to give Candidate 3 an additional 200 votes in every region, we would
say
for ( int i = 0; i != 3; i = i + 1 )
{ election[i][3] = election[i][3] + 200; }
To print each candidate’s grand total of votes, we write a nested for-loop that
totals each column of the matrix:
for ( int j = 0; j != 4; j = j + 1 )
{ int votes = 0;
for ( int i = 0; i != 3; i = i + 1 )
{ votes = votes + election[i][j]; }
System.out.println("Candidate " + j + " has " votes + " votes");
}
The previous for loop displays the standard pattern for examining each and every
element of a matrix. Yet another example is printing the total votes cast in each
region of the election, which requires that we total each row of the matrix:
for ( int i = 0; i != 3; i = i + 1 )
{ int total = 0;
for ( int j = 0; j != 4; j = j + 1 )
{ total = total + election[i][j]; }
System.out.println(total + " votes were cast in Region " + i);
}
In the above example, the order of the loops is reversed, because rows are traversed,
rather than columns.
Exercises
1. Create a two-dimensional array of integers, m, with 12 rows and 14 columns and
initialize it so that each m[i][j] holds i*j.
3. Given this array, int[][] r = new int[4][4], and given this nested for-loop
that prints r’s contents,
for ( int i = 0; i != 4; i = i + 1 )
{ for ( int j = 0; j != 4; j = j + 1 )
{ System.out.print( r[i][j] + " " ); }
System.out.println();
}
write for-loops that initialize r so that the following patterns are printed:
(a) 1 0 0 0
1 2 0 0
1 2 3 0
1 2 3 4
(b) 1 2 3 4
0 3 4 5
0 0 5 6
0 0 0 7
(c) 1 0 1 0
0 1 0 1
1 0 1 0
0 1 0 1
4. Modify the application in Figure 1 to use the election array. (Of course, a vote
must be cast with a region number and a candidate number.)
a1 : int[3] [ ]
int[ ] [ ] election == a1 0 a2
1 a3
2 a4
This exposes that each row of matrix election is itself a (one-dimensional) array.
Alas, we cannot treat a matrix’s columns in a similar way.
Another consequence of the storage layout is that the “length” of a matrix is the
number of rows it possesses. For the above example,
election.length
computes to 3. To learn the number of columns in a matrix, we ask for the length of
one of the matrix’s rows. For example,
436
election[0].length
computes to 4.
Finally, some care must be taken when asking for the number of columns of a
two-dimensional array. Consider this odd example:
double[][] ragged = new double[4][];
double[0] = new double[2];
double[2] = new double[1];
double[3] = new double[0];
The first statement constructs an array variable, ragged, that will have four rows and
an undetermined number of columns; it looks like this in storage:
a1 : double[4][]
The following three statements construct one-dimensional array objects and assign
them to ragged’s elements, giving us
a1 : double[4][]
Two-dimensional arrays prove helpful for modelling game boards in computer games.
As an example, we design and build a slide puzzle, which is a game board that holds
numbered pieces that are moved by the user, one piece at a time.
Behavior
The behavior of the puzzle game goes like this: the puzzle starts in this configuration:
The user instructs the puzzle to move a piece by typing a number (in this configura-
tion, only 1 or 4 would be allowed), and the game responds by moving the requested
438
SlidePuzzleBoard PuzzlePiece
private PuzzlePiece[][] board 1 * private int face value
move(int w): boolean
piece:
The user may request similar moves for as long as she wishes.
Exactly one element within array board must be empty (hold null), and it is conve-
nient to declare two fields, empty row and empty col, to remember the coordinates of
the empty space.
Method move(w) must move the piece labelled by integer w into the empty space.
For the move to succeed, the piece labelled by w must be adjacent to the empty space.
This means the algorithm for move(int w) must perform the appropriate checks:
1. If the playing piece labelled by w is immediately above the empty space (marked
by empty row and empty col), or if it is immediately below the empty space, or
immediately to the left or right of the empty space,
2. Then, move the piece labelled by w to the empty space, and reset the values of
empty row and empty col to be the position formerly occupied by w’s piece.
To write Step 1, we can make good use of this helper function, which looks at position,
row, col, to see if the piece labelled w is there:
/** found returns whether the piece at position row, col is labeled w */
private boolean found(int w, int row, int col)
Then, Step 1 can be coded to ask the helper function to check the four positions
surrounding the empty space whether piece w is there. See Figure 13 for the completed
coding of method move.
The board’s constructor method creates the board array and fills it with newly
created puzzle pieces. Finally, we add a method that returns the contents of the
440
puzzle board. (This will be useful for painting the board on the display) The contents
method returns as its result the value of the board. It is best that contents not return
the address of its array board. (If it did, the client that received the address could
alter the contents of the array!) Instead, a new two-dimensional array is created, and
the addresses of the playing pieces are copied into the new array.
1. tells the PuzzleWriter component to paint the current state of the puzzle;
Exercises
1. Test class SlidePuzzleBoard by creating an object, board, from it and imme-
diately asking its contents method for the the board. Display the board with
these loops:
PuzzlePiece[][] r = board.contents();
for ( int i = 0; i != r.length; i = i+1 )
{ for ( int j = 0; j != r[i].length; j = j+1 )
{ if ( r[i][j] == null )
8.10. CASE STUDY: SLIDE-PUZZLE GAME 441
/** found returns whether the piece at position row, col is labeled v */
private boolean found(int v, int row, int col)
{ boolean answer = false;
if ( row >= 0 && row < size && col >= 0 && col < size )
{ answer = ( board[row][col].valueOf() == v ); }
return answer;
}
}
8.10. CASE STUDY: SLIDE-PUZZLE GAME 443
PuzzleController
play() PuzzleWriter
displayPuzzle()
JOptionPane printError(String s)
SlidePuzzleBoard PuzzlePiece
private PuzzlePiece[][] board 1 * private int face value
move(int w): boolean
{ System.out.print("X "); }
else { System.out.print( r[i][j].valueOf() + " " ); }
}
System.out.println();
}
Next, use the object’s move method to ask the board to move several numbers.
Display the board resulting from each move.
k = k + 1;
}
}
return answer;
}
}
We wish to verify that the method behaves properly for all possible arguments. We
have success for simple test cases, like this one,
int[] test0 = new int[10];
test0[3] = 3;
exchange(test0, 4);
But what other tests should we attempt? To answer this, we should list all the
indexings of array r that appear in the method—they are r[i] and r[i - 1]—and
we should predict the range of values that the index expressions, i and i - 1, might
have. Remember that the values must fall in the range, 0 to r.length - 1. Now, do
they?
A bit of thought lets us invent this test,
int[] test1 = new int[10];
test0[0] = 3;
exchange(test0, 0);
which generates an exception, because i - 1 has a value that is invalid for array r.
Another test case,
int[] test1 = new int[10];
test0[9] = 3;
exchange(test0, 10);
There is, alas, one more test that exposes an error that goes beyond index-value
calculation:
int[] test2 = null;
exchange(test2, 1);
This invocation of exchange leads to an exception when the array argument is refer-
enced. If we are uncertain that we can validate that all the method’s invocations are
with proper arrays, then we must add one more test:
448
Testing the values of array indexes becomes harder still when loops are used to
examine an array’s elements, because it is crucial that the loop starts and terminates
appropriately. Consider again the first loop we saw in this chapter, which attempts
to select the largest number in an array:
double high_score = score[0];
for ( int i = 1; i <= 5; i = i + 1 )
{ if ( score[i] > high_score )
{ high_score = score[i]; }
}
System.out.println(high_score);
We note that the only array indexing is score[i], and we readily see that the range
of values denoted by index i is 0 to 5. But this range is sensible only if we are certain
that the length of array score is at least 6—indeed, it should equal 6. It is better to
use score.length in the loop’s termination test:
double high_score = score[0];
for ( int i = 1; i < score.length; i = i + 1 )
{ if ( score[i] > high_score )
{ high_score = score[i]; }
}
System.out.println(high_score);
This ensures that the loop correctly examines all the elements of the array to select
the high score.
The general strategy for testing a component that uses arrays goes as follows:
1. Validate that every array-typed variable, r, is indeed assigned an array object as
its value;
2. For every indexing expression, r[e], calculate the range of values to which e
might evaluate, testing these values and especially 0 and r.length.
8.12 Summary
Here are the main points to remember from this chapter:
8.12. SUMMARY 449
New Constructions
• one-dimensional array (from Figure 1):
int num_candidates = 4;
int[] votes = new int[num_candidates];
...
votes[v] = votes[v] + 1;
• array-length attribute:
New Terminology
• array: an object that holds a collection of values, called elements, of the same
data type (e.g., a collection of integers or a collection of JPanel objects). The
elements are named or indexed by nonnegative integers. (See the above exam-
ples.)
• key: the identity code used to retrieve a data value saved in a database.
Points to Remember
• In Java, an array is an object that must be constructed with the new keyword,
e.g., int[] r = new int[6], which creates an array that can hold 6 integers,
indexed from 0 to 5.
• Individual array elements are indexed by expressions and are used like ordinary
variables, e.g., r[i + 1] = 2 * r[i].
• The elements of an array can be (the addresses of) objects as well, e.g., Card[]
deck = new Card[52] is an array that can hold 52 Card objects. When the array
is constructed, it holds no objects—all elements have value null. The objects
held in the array must be explicitly constructed and assigned to the array’s
elements, e.g., deck[0] = new Card(Card.HEARTS, Card.QUEEN).
(a) Each candidate has a name, an address, and an age. The application reads
this information first, saves it in objects, and uses the information to count
votes, which are now submitted by typing the candidates’ names.
(b) The election becomes a national election in 3 regions. Make the application
display the total vote counts for each region as well as the total vote counts
for each candidate.
8.13. PROGRAMMING PROJECTS 451
2. The classic algorithm for calculating the prime numbers in the range 2..n is
due to Eratosthenes:
3. With the help of arrays, we can improve the output views for bar graphs, point
graphs, and pie charts from the Programming Projects in Chapter 5. Reprogram
each of the following.
a e i m
b f j n
8.13. PROGRAMMING PROJECTS 453
c g k x
d h l x
For example, for input key 421 and abcdefghijklmn, a 5-by-3 matrix would be
built:
4 2 1
-----
a f k
b g l
c h m
d i n
e j x
1 2 4
-----
k f a
l g b
m h c
n i d
x j e
and the output are the words, kfa, lgb, mhc, nid, and xje.
Write programs to encode and decode messages with this algorithm.
454
6. For each of the following entities, design a class that models the entity. Most
of the entity’s attributes are listed; feel free to add more. Design appropriate
methods.
(a) a library book: the book’s name, its author, its catalog (id) number, and
the id number of the person (if anyone) who has borrowed the book, the
due date for the book to be returned, and the number of times the book
has been borrowed.
(b) a patron’s library information: the patron’s name, address, id number, and
the catalog numbers of all books currently loaned to the person (maximum
of 6).
(c) an appointment record: the appointment’s date, time of day, and topic
(d) an inventory record: the name of a sales item, its id number, its wholesale
price, its retail price, and the quantity in stock.
(e) a purchase record: the id number of the purchaser, the (id numbers of the)
items and quantities ordered of each, and the means of payment/
(f) a purchaser (customer) record: the id number, name, and address of a
customer, the id numbers of the customer’s outstanding orders, and the
customer’s purchase history for the past 12 months
(g) an email message: the address of its sender, the address of its receiver, the
message’s subject, and its body (text)
7. Use the classes you defined in the previous exercise plus class Database from
Figure 4 to build the following applications:
(a) A library application, which maintains a database for the library’s books
and a database for the library’s borrowers. Both databases must be used
when books are borrowed and returned.
(b) A business accounting application, which uses databases of inventory records,
purchases, and purchasers. The databases are used when customers pur-
chase items.
(c) An email postal service, which allows multiple users to login, send, and
receive email messages to/from one another.
8. Here are the rules for a simple card game: A player tries to obtain two cards
that total the highest possible score, where a card’s “score” is its count. (For
simplicity, ace is worth 1, 2 is worth 2, ..., king is worth 13.) The dealer gives
each player 2 cards. A player can surrender at most one card and accept a third
card as a replacement. Then, all players must reveal their hands.
8.13. PROGRAMMING PROJECTS 455
9. Here are the rules for playing the card game, “21”: A player tries to collect
cards whose total score is 21 or as close as possible. The player with the highest
score not exceeding 21 wins. (If the player’s score is higher than 21, the player
is “busted” and loses.) A card’s has a point value based on its count (e.g., a
four of clubs is valued 4), but face cards are valued 10, and we will say that an
ace is valued 11.
Initially, each player receives two cards from the dealer. Then, each player can
request additional cards, one at a time, from the dealer. After all the players
have received desired additional cards, the players reveal their hands.
Write an application that lets a computerized dealer, a human player, and a
computerized player play 21. The computerized player should be smart enough
to request additional cards as long as the player’s total score is 16 or less.
(a) The dealer is also a player; therefore, the dealer must deal herself a hand.
(b) An ace has a value of either 1 or 11, based on the discretion of the player
who holds the card.
(c) In some casinos, a dealer deals from two decks of cards. Alter the applica-
tion to deal alternately from two decks.
(d) If a player’s first two cards have identical counts (e.g., two eights or two
queens), the player can “split” the cards into two distinct hands and con-
tinue with two hands.
(e) The game lets the players play multiple rounds. In particular, this means
that the deck of cards must be “reshuffled” with the cards not in the
players’ hands when the deck is emptied of cards in the middle of a round.
11. Write an application that plays tic-tac-toe (noughts and crosses) with the user.
point, and she is allowed to uncover two more letters. If the letters fail to match,
they are recovered and the next player tries. The players take turns until all
the letter pairs are uncovered. The player with the most points wins.
Build a computerized version of this game.
13. Write an application that lets two human players play checkers.
14. Write an animation that lets the computer play the game of “Life.” The game
goes as follows: the user specifies the size of game board, a matrix, and also
gives the starting positions of where some pebbles (“cells”) live. Every second,
the computer updates the board, creating and removing cells, according to the
following rules:
15. Write an appointments manager program. The program stores and retrieves
appointments that are listed by date and hour of day for one full week. (For
simplicity, assume that at most one appointment can be scheduled per hour.)
Include input commands for inserting appointments, listing the appointments
for a given day, deleting appointments, and printing appointments.
16. Write an application that reserves seats in an airplane based on input requests
in the following format:
• number of seats desired (should be seated in the same row, next to one
another, if possible)
• first class or economy
• aisle or window seat (if two or more seats requested, one seat should meet
this preference)
8.13. PROGRAMMING PROJECTS 457
17. Write a program that plays the card game, “War”: There are two players; one
is human, the other is computerized. Here are the rules: each player gets a
hand of 10 cards. The players play 10 rounds; each round goes as follows:
(a) A player places one of the cards from her hand face up on the table.
(b) The other player does the same.
(c) The player whose card has the larger value of the two cards takes all the
cards on the table and places them in her “winnings pile.” (Note: the
definition of “value” is given below. A “winnings pile” is a stack of cards
that is not used any more in the game. Each player keeps her own winnings
pile.) The winning player must start the next round.
(d) If the two cards on the table have the same value (and this is called a
“War”) , then all the cards on the table remain there for the next round.
The player who started this round must start the next round.
After all ten rounds are played, the winner is the player with more cards in her
winnings pile.
Here is the definition of “value”: regardless of suit, 2 has the lowest value, then
3, then 4, etc., then 9, then 10, then jack, then queen, then king, then ace.
There is a minimal amount of strategy that a player uses to win at War. Your
strategy for the computerized player must be at least this smart: (1) If the
computerized player plays the first card of a round, then any card remaining in
the computerized player’s hand can be played. (2) If the computerized player
plays the second card of a round, then the computerized player plays a card in
its hand whose value is greater than or equal to the value of the card that the
human just played. If the computerized player has no such card, then it plays
any card in its hand.
18. Choose another card game of your choosing, e.g., “Hearts” or “Crazy Eights”
and model it as a computer game. Or, chose a game that uses dice and imple-
ment it as a computer game.
19. Write an application that performs bin packing: The input consists of a se-
quence of “packages” whose sizes are coded as nonnegative integers along with
a sequence of “bins” whose capacities are are coded also by an integer. (For sim-
plicity, we assume that all bins have the same capacity.) The program assigns
each package to a bin such that no bin’s capacity is exceeded. The objective
is to use the minimum number of bins to hold all the packages. Attempt these
implementations:
(a) Smallest packages first: The packages are sorted by size and smallest pack-
ages are used first.
458
(b) Largest packages first: The packages are sorted by size and largest packages
are used first.
(c) Random filling: The packages are used in the order they appear in the
input.
8.14.2 Searching
These optional sections expand upon the concepts presented in this chapter. In partic-
ular, we emphasize using arrays to sort collections of numbers and efficiently search
for numbers in a sorted collection.
8.14.1 Sorting
When an array is used as a database, where elements are fetched and updated fre-
quently, there is a distinct advantage to ordering the elements by their keys—it be-
comes far easier to locate an element. The process of ordering an array’s elements is
called sorting.
Algorithms for sorting have a rich history, and we cannot do justice here. Instead,
we focus upon the development of two traditional sorting methods, selection sort and
insertion sort. To simplify the algorithms that follow, we work with arrays of integers,
where we sort the elements so that they are ordered in value from smallest integer
to largest. (Of course, we can use the same techniques to sort elements by their key
values.)
The idea behind selection sort is simple: Locate the least integer in the array, and
move it to the front. Then, find the next least integer, and move it second to the
front. Repeat this process until all integers have been selected in order of size. The
algorithm that sorts array r in this manner goes
8.14. BEYOND THE BASICS 459
When selection sorting starts, its loop finds the least element in the range r[0]..r[4]
at index 2 and exchanges the elements at indexes 0 and 2:
0 1 2 3 4
r -2 8 11 7 10
The second loop iteration locates the least element in the range r[1]..r[4] at index
3, and the elements at indexes 1 and 3 are exchanged:
0 1 2 3 4
r -2 7 11 8 10
The algorithm next considers the elements in range r[2]..r[4] and so on until it
reaches this end result:
0 1 2 3 4
r -2 7 8 10 11
we see that the algorithm first inserts the 8 where it belongs with respect to the 11:
0 1 2 3 4
r 8 11 -2 7 10
This makes the prefix, r[0]..r[1], correctly sorted. Next, the -2 must be inserted
in its proper place with respect to the sorted prefix:
0 1 2 3 4
r -2 8 11 7 10
To make room for -2 at its proper position, r[0], the two elements, 8 and 11, must
be shifted one position to the right. Now, r[0]..r[2] is correctly sorted. The last
two elements are inserted similarly.
8.14. BEYOND THE BASICS 461
Figure 16 show the insertion sorting method. The method’s most delicate step is
searching the sorted prefix to find a space for v—the while-loop searches from right
to left, shifting values one by one, until it encounters a value that is not larger than v.
At all iterations, position r[j] is reserved for v; when the iterations stop, v is inserted
at r[j].
Exercises
1. Try selection sort and insertion sort on these arrays: {4, 3, 2, 2}; {1, 2, 3,
4}; {1}; { } (the array of length 0).
2. Explain which of the two sorting methods might finish faster when the array to
be sorted is already or nearly sorted; when the array’s elements are badly out
of order.
3. Explain why the for-loop in method selectionSort iterates one more time than
it truly needs.
5. Another sorting technique is bubble sort: over and over, compare pairs of ad-
jacent elements and exchange them if the one on the right is less than the one
462
on the left. In this way, the smaller elements move like “bubbles” to the left
(“top”) of the array. The algorithm goes:
8.14.2 Searching
Once an array is sorted, it becomes simpler to locate an element within it—rather
than examining items one by one, from left to right, we can start searching in the
middle, at approximately where the item might appear in the sorted collection. (This
is what we do when we search for a word in a dictionary.) A standard searching
algorithm, called binary search, exploits this idea.
Given a sorted array of integers, r, we wish to determine where a value, item, lives
in r. We start searching in the middle of r; if item is not exactly the middle element,
we compare what we found to it: If item is less than the middle element, then we
next search the lower half of the array; if item is greater than the element, we search
the upper half of the array. We repeat this strategy until item is found or the range
of search narrows to nothing, which means that item is not present.
The algorithm goes
Figure 17 shows the method, which is a standard example of the searching pattern of
iteration.
If we searched for the item 10 in the sorted array r seen in the examples in
the previous section, the first iteration of the loop in binarySearch gives us this
8.14. BEYOND THE BASICS 463
configuration:
0 1 2 3 4 int lower == 0
int upper == 4
r -2 7 8 10 11 int index == 2
The search starts exactly in the middle, and the loop examines r[2] to see if it is 10.
It is not, and since 10 is larger than 8, the value found at r[2], the search is revised
as follows:
0 1 2 3 4 int lower == 3
int upper == 4
r -2 7 8 10 11 int index == 3
Searching the upper half of the array, which is just two elements, moves the search
to r[3], which locates the desired item.
Notice that a linear search, that is,
int index = 0;
464
Exercises
1. Use the binary search method in Figure 17 on the sorted array, {1, 2, 2, 4,
6}: Ask the method to search for 6; for 2; for 3. Write execution traces for these
searches.
8.14. BEYOND THE BASICS 465
Explain why the invariant and the termination of the loop ensure that the
method returns a correct answer. Explain why the loop must terminate. (This
is not trivial because the loop makes one extra iteration before it quits.)
elements, then one examination in the middle of the remaining 128 elements, then
one examination in the middle of the remaining 64 elements, and so on—a maximum
of only 9 examinations are required!
We can state this behavior more precisely with a recursive definition. Let E(N)
stand for the number of examinations binary search makes (in worst case) to find an
item in an array of N elements.
Here is the exact number of examinations binary search does:
The first equation states that a search of an array with multiple elements requires
an examination of the array’s middle element, and assuming the desired item is not
found in the middle, a subsequent search of an array of half the length. An array of
length 1 requires just one examination to terminate the search.
To simplify our analysis of the above equations, say the array’s length is a power
of 2, that is, N = 2M , for some positive M. (For example, for N = 256, M is 8. Of course,
not all arrays have a length that is exactly a power of 2, but we can always pretend
that an array is “padded” with extra elements to make its length a power of 2.)
Here are the equations again:
After several calculations with this definition (and a proof by induction—see the
Exercises), we can convince ourselves that
E(2M ) = M + 1
From number theory (and an induction proof), we can discover that this sequence
totals
N * (N - 1)
-------------
2
that is, (1/2)N 2 − (1/2)N . When N has a substantial positive value, only the N 2 factor
matters, so we say that the algorithm has order N 2 (quadratic) time complexity.
Algorithms with quadratic time complexity perform significantly slower than log-
arithmic and linear algorithms, and this slowness can be annoying when N is very
large (e.g., for N equals 100, N2 is 10,000).
It is easy to see that selection sort does exactly N-1 exchanges of elements—a
linear time complexity—so the exchanges are not the costly part of the algorithm.
Next, we consider insertion sort (Figure 16); recall that it shifts elements, one
by one, from right to left into their proper places. In worst case, insertion sort
encounters an array whose elements are in reverse order. In this case, the algorithm’s
first iteration makes one comparison and one exchange; the second iteration makes
two comparisons and two exchanges; and so on. The total number of comparisons
and exchanges are the same, namely,
1 + 2 + ... + (N-2) + N-1
In contrast, insertion sort does badly at exchanging elements when sorting an ar-
bitrary array—it makes order N2 exchanges, whereas selection sort limits its exchanges
to at worst order N. Therefore, selection sort is preferred if there is substantial dif-
ficulty in moving elements of the array. (But this is not normally the case for Java
arrays, because the elements of a Java array are either primitive values, like numbers,
or addresses of objects. These values are easy to exchange.)
Exercises
1. To get intuition about time complexities, calculate the values of N, 5*N, log N,
N2 , and (1/2)(N 2 ) − (1/2)N for each of the following values of N: 4; 64; 128; 512;
1024; 16384.
Then, reexamine the time complexities of the searching and sorting algorithms
and describe how the algorithms would behave on arrays of size N, for the above
values of N. (To give some perspective to the analysis, pretend that your com-
puter is very slow and takes 0.1 seconds to perform a comparison or exchange
operation.)
2. Modify class Database in Figure 3 so that its insert method sorts the base
array after a new record is added. (Warning—watch for null values in the
array!) Because the contents of base are already sorted when a new element is
inserted, does this simplify the sorting process? What form of sorting is better
for this application—selection sort or insertion sort?
Next, modify locationOf so that it uses binary search.
linear search; for binary search. Are your answers significantly different than
before?
Merge sort
Sorting can be accomplished with a divide-and-conquer algorithm, which proceeds as
follows: To sort a complete array, r,
1. Divide the array into two smaller segments, call them s1 and s2.
2. Sort s1.
3. Sort s2.
4. Merge the two sorted segments to form the completely sorted array.
The merge step goes as follows: Say that you have a deck of cards you wish to sort.
You divide the deck in half and somehow sort each half into its own pile. You merge
the two piles by playing this “game”: Turn over the top card from each pile. (The
top cards represent the lowest-valued cards of the two piles.) Take the lower-valued
of the two cards, form a new pile with it, and turn over the next card from the pile
from which you took the lower-valued card. Repeat the game until all the cards are
moved into the third pile, which will be the entire deck, sorted.
Figure 19 shows the method based on this algorithm, called merge sort. Like
the recursive version of binary search, mergeSort is first invoked as mergeSort(a, 0,
8.14. BEYOND THE BASICS 471
/** merge builds a sorted array by merging its two sorted arguments
* @param r1 - the first sorted array
* @param r2 - the second sorted array
* @return a sorted array whose elements are exactly those of r1 and r2 */
private int[] merge(int[] r1, int[] r2)
{ int length = r1.length + r2.length;
int[] answer = new int[length];
int index1 = 0;
int index2 = 0;
for ( int i = 0; i != length; i = i+1 )
// invariant: answer[0]..answer[i-1] is sorted and holds the elements of
// r1[0]..r1[index1-1] and r2[0]..r2[index2-1]
{ if ( index1 == r1.length
|| ( index2 != r2.length && r2[index2] < r1[index1] ) )
{ answer[i] = r2[index2];
index2 = index2 + 1;
}
else { answer[i] = r1[index1];
index1 = index1 + 1;
}
}
return answer;
}
472
a.length-1) to indicate that all the elements in array a should be sorted. The method
returns a new array that contains a’s elements reordered.
Method mergeSort first verifies that the segment of the array it must sort has at
least two elements; if it does, the segment is divided in two, the subsegments are
sorted, and merge combines the two sorted subarrays into the answer.
The time complexity of merge sort is is significantly better than the other sorting
algorithms seen so far; we consider the number of comparisons the algorithm makes.
(The analysis of element exchanges goes the same.)
First, we note that merge(r1, r2) makes as many comparisons as there are ele-
ments in the shorter of its two array parameters, but it will be convenient to over-
estimate and state that no more than r1.length + r2.length comparisons are ever
made.
Next, we define the comparisons made by mergeSort on an array of length N as
the quantity, C(N):
C(N) = C(N / 2) + C(N / 2) + N, if N > 1
C(1) = 0
The first equation states that the total comparisons to sort an array of length 2 or
more is the sum of the comparisons needed to sort the left segment, the comparisons
needed to sort the right segment, and the comparisons needed to merge the two sorted
segments. Of course, an array of length 1 requires no comparisons.
Our analysis of these equations goes simpler if we we pretend the array’s length
is a power of 2, that is N = 2M , for some nonnegative M:
These equations look like the ones discovered in the analysis of binary search.
Indeed, if we divide both sides of the first equation by 2M , we see the pattern in the
binary search equation:
C(2M ) C(2M −1 )
= +1
2M 2M −1
As with the binary search equation, we can conclude that
C(2M )
=M
2M
When we multiply both sides of the above solution by 2M , we see that
C(2M ) = 2M ∗ M
C(N ) = N ∗ logN
8.14. BEYOND THE BASICS 473
We say that merge sort has order N log N time complexity. Such algorithms perform
almost as well as linear-time algorithms, so our discovery is significant.
Alas, mergeSort suffers from a significant flaw: When it sorts an array, it creates
additional arrays for merging—this will prove expensive when sorting large arrays.
The method in Figure 19 freely created many extra arrays, but if we are careful, we
can write a version of mergeSort that creates no more than one extra array the same
size as the original, unsorted array. For arrays that model large databases, even this
might be unacceptable, unfortunately.
Quicksort
A brilliant solution to the extra-array problem was presented by C.A.R. Hoare in
the guise of the “quicksort” algorithm. Like merge sort, quicksort uses the divide-
and-conquer technique, but it cleverly rebuilds the sorted array segments within the
original array: It replaces the merge step, which occurred after the recursive invoca-
tions, with a partitioning step, which occurs before the recursive invocations.
The idea behind partitioning can be understood this way: Say that you have a
deck of unsorted playing cards. You partition the cards by (i) choosing a card at
random from the deck and (ii) creating two piles from the remaining cards by placing
those cards whose values are less than the chosen card in one pile and placing those
cards whose values are greater than the chosen card in the other.
It is a small step from partitioning to sorting: If you sort the cards in each pile,
then the entire deck is sorted by just concatenating the piles. This is a classic divide-
and-conquer strategy and forms the algorithm for quicksort. Given an array, r, whose
elements are numbered r[lower] to r[upper]:
It is essential that both of the partitions created by partition are nonempty. For
this reason, a conditional statement after the while-loop asks whether the partition
of elements less than the pivot is empty. If it is, this means the pivot is the smallest
value in the subarray, no exchanges were made, and the pivot remains at r[lower].
In this case, the pivot value itself becomes the first partition.
We can see partitioning at work in an example. Say that we invoke quickSort(r,
0 ,6), which immediately invokes partition(r, 0, 6) for the array r shown below.
The variables in partition are initialized as follows:
0 1 2 3 4 5 6 int v == 5
int i == 0
r 5 8 4 1 7 3 9 int m == -1
m i
We position m and i under the array to indicate the variables’ values. The pivot value
is r[0]—5. Values less than the pivot will be moved to the left; the other values will
move to the right.
Within partition’s while-loop, i moves right, searching for a value less than 5; it
finds one at element 2. This causes r[2] to be moved to the end of the first partition—
it is exchanged with r[m+1], and both m and i are incremented. Here is the resulting
situation:
0 1 2 3 4 5 6
r 4 8 5 1 7 3 9
m i
A check of the loop invariant verifies that the elements in the range r[0] to r[m] are
less than the pivot, and the values in the range r[m+1] to r[i-1] are greater-or-equal
to the pivot.
Immediately, i has located another value to be moved to the first partition. An
exchange is undertaken between r[1] and r[3], producing the following:
0 1 2 3 4 5 6
r 4 1 5 8 7 3 9
m i
The process continues; one more exchange is made. When the method finishes, here
is the partitioned array:
0 1 2 3 4 5 6
r 4 1 3 8 7 5 9
m
and we invoked partition(r, 0, 6), then partition would choose the pivot to be 1
and would create the partitions r[0] and r[1]..r[6]. The subsequent recursive invo-
cation to quickSort(r, 1, 6) causes another such partitioning: r[1] and r[2]..r[6].
This behavior repeats for all the recursive calls.
In a case as the above, quickSort degenerates into a variation of insertion sort
and operates with order N2 time complexity. Obviously, if quickSort is applied often
to sorted or almost-sorted arrays, then partition should choose a pivot value from
the middle of the array rather than from the end (see the Exercises below). Studies of
randomly generated arrays shows that quicksort behaves, on the average, with order
N log N time complexity.
Exercises
1. To gain understanding, apply iterative binarySearch in Figure 17 and recursive
binarySearch in Figure 18 to locate the value, 9, in the array, int[] r = {-2,
5, 8, 9, 11, 14}. Write execution traces.
3. Rewrite mergeSort in Figure 19 so that it does not create multiple new arrays.
Instead, use this variant:
{ ...
mergeSort(r, scratch, lower, middle);
mergeSort(r, scratch, middle+1, upper);
...
}
4. Finish the execution traces for the example in this section that uses quickSort.
5. Write a partition algorithm for use by quickSort that chooses a pivot value in
the middle of the subarray to be partitioned.
The syntax allows one- and multi-dimensional array types, e.g., int[] as well as
GregorianCalendar[][].
Array Constructors
Array variables are declared like ordinary variables; within the initialization state-
ment, we can construct an array object explicitly, e.g., int[] r = new int[4] or by
means of a set-like initialization expression, e.g., int[] r = {1, 2, 4, 8}. Here is a
syntax definition that includes the two formats:
(Recall that [[ E ]]? means that a phrase, E, is optional and [[ E ]]* means that
phrase E can be repeated zero or more times.)
The syntax, { [[ INITIAL EXPRESSION LIST ]]? } defines the set notation for ar-
ray object construction. The syntax makes clear that multi-dimensional arrays can
be constructed from nested set expressions:
This constructs an array with three rows of varying lengths and assigns it to d.
An array object can be constructed by a set expression only within an initialization
statement. The compiler verifies that the dimensions of the set expression and the
data types of the individual elements in the set expression are compatible with the
data type listed with the variable declared. Only elements of primitive type can be
listed.
An array constructed with the new keyword is defined by means of an OBJECT CONSTRUCTION
of the form, new ARRAY ELEMENT TYPE DIMENSIONS:
That is, ARRAY ELEMENT TYPE, the data type of the array’s individual elements, is
listed first, followed by the all the array’s dimensions. The quantity of at least the
first dimension must be given; the quantities of the dimensions that follow can be
omitted. For example, new int[4][] constructs a two-dimensional array object with
4 rows and an unspecified number of columns per row, and new int[4][3] constructs
a two-dimensional array object with 4 rows and 3 columns. The phrase, new int[],
is unacceptable.
An array construction, new ARRAY ELEMENT TYPE DIMENSIONS, is type checked to
validate that all expressions embedded in the DIMENSIONS have data types that are sub-
types of int. The compiler calculates the data type of the phrase as ARRAY ELEMENT TYPE
followed by the number of dimensions in DIMENSIONS.
The execution semantics of an array construction goes as follows: For simplicity,
consider just a one-dimensional object, new ARRAY ELEMENT TYPE[EXPRESSSION]:
2. An object is constructed with v distinct elements. The data type, ARRAY ELEMENT TYPE[v],
is saved within the object. Say that the object has storage address, a.
8.14. BEYOND THE BASICS 479
3. If ARRAY ELEMENT TYPE is a numeric type, the elements in the object are initialized
to 0. If it is boolean, the elements are initialized to false. Otherwise, the
elements are initialized to null.
Recall that VARIABLE phrases must compute to addresses of storage cells (to which
are assigned values); RECEIVERs must compute to addresses of objects that can receive
messages; and EXPRESSIONs must compute to values that can be stored in cells.
The syntax allows an array to use multiple indexes, e.g., d[3][2] = 4.5. More
importantly, since an array is an object, it is a “receiver” of messages that ask for
indexings, e.g., r[0] sends a “message” to the object named r, asking it to index itself
at element 0 and return the value in that cell.
For an indexing expression, RECEIVER[EXPRESSION], the compiler verifies that the
data type of EXPRESSION is a subtype of int, and it verifies that the data type of
RECEIVER is an array type. When the indexing expression appears as a VARIABLE on
the left-hand side of an assignment, the compiler verifies, as usual, that the data type
of the right-hand side expression is a subtype of the left-hand side variable’s type.
When used as a VARIABLE on the left-hand side of an assignment, the semantics
of the phrase, RECEIVER[EXPRESSION], computes an address:
3. If v is nonnegative and is less than the length of the array at address a, then
the address, a[v], is returned as the result; otherwise, an exception is thrown.
480
For example, say that r holds the address, a1, of an array object. Then, the assign-
ment, r[1 + 2] = 4, causes r[1 + 2] to compute to the address, a1[3], and inserts
4 into the cell at that address.
When the phrase, RECEIVER[EXPRESSION], is used as a RECEIVER or as an EXPRESSION,
then of course the addressed cell is dereferenced and the value in the cell is returned
as the result. For example, System.out.println(r[3]) prints the value found in the
cell addressed by r[3].
Here is a more complex example. For the arrays,
a1 : int[2][ ] a2 : int[4]
int[ ] r == a1 0 1 0 1 2 3
a2 null 0 2 0 7
int[ ] s == a2
(a) Since r has value a1, and r[0] is the receiver of the message, [s[1] + 1],
the address, a1[0] is dereferenced to the value a2.
(b) The expression, s[1] + 1, computes to 3, because s has value a2, s[1]
appears as an expression, hence the address a2[1] is dereferenced to 2 and
1 is added to it.
(c) Because r[0] computed to a2 and s[1] + 1 computed to 3, the address
a2[3] is formed. Since this appears as an expression, it is dereferenced to
produce 7.
In most programming languages, Step 4 is not required, because the type checking
already performed by the compiler suffices. But the additional type checking at
execution is forced upon Java because of Java’s subtyping laws for object (reference)
types.
To see this, here is an example. Perhaps we write this method:
The Java compiler examines the method and judges it acceptable. Next, we write
this class:
This class is also acceptable to the Java compiler. But now, we play a trick:
Programming to Interfaces
9.1 Why We Need Specifications
9.3 Inheritance
9.8 Packages
9.10 Summary
When we write a class that matches a specification, we say that the class imple-
ments the specification. In this chapter, we learn four Java constructions for designing
classes and connecting them to their collaborating classes:
• Java interfaces, which define specifications that a coded class must implement.
• Java inheritance (extends), which defines a new class by adding methods onto
an already written class;
484
• Java abstract classes, which are “incomplete classes,” part specification and
part implementation.
This chapter’s title comes from a slogan that practicing programmers follow when
they build an application:
That is, when you write a class that depends on another, collaborator class, you should
rely on the collaborator’s interface—its specification—and not its coding (its imple-
mentation). With this approach, you can design and implement classes separately yet
ensure that the assembled collection of classes collaborate successfully.
• The specification told the user which component must be fitted to the player
to ensure correct operation.
• The specification told the manufacturer of the disc player what size to build
the player’s battery chamber and what voltage and amperage to use within the
player’s electronics.
• The specification told the battery manufacturer what size, voltage, and amper-
age to build batteries so that others can use them.
These three facts are important in themselves, but they also imply that the user, the
disc manufacturer, and the battery manufacturer need not communicate directly with
each other — the specification of the battery is all that is needed for each party to
perform its own task independently of the other two.
Without size specifications of items like batteries, clothing, and auto parts, every-
day life would would be a disaster.
9.2. JAVA INTERFACES 485
When we assemble a program from components, the components must fit together.
When a class, A, invokes methods from an object constructed from class B, class A
assumes that B possesses the methods invoked and that the methods behave in some
expected way — in the way they are specified. Just like batteries have specifications
(e.g., sizes AAA, AA, C,...), classes have specifications also. This explains why we
have been writing specifications for the Java classes we code.
For example, in Chapter 7, we encountered a case study of a simulation of a ball
bouncing in a box. The specifications of the ball and the box proved crucial to both
writing the simulation’s classes and fitting the classes together in the program.
The Java language and the Java compiler can help us write specifications of classes
and check that a class correctly matches (implements) its specification. In this chapter
we study several Java constructions for designing programs in separate classes:
1. the interface construction, which lets us code in Java the information we spec-
ify in a class diagram;
3. the abstract class construction, which lets us code an incomplete class that
can be finished by another class.
Finally, to help us group together a collection of related classes into the same folder,
we use Java’s package construction.
We will study each of the constructions in turn in this chapter.
• We write classes that match or implement the interface, and the Java compiler
verifies this is so.
• We write classes that rely upon the interface, and the Java compiler verifies
this is so.
These ideas are best explained with a small example: Say that you and a friend must
write two classes—one class models a bank account and the other class uses the bank
account to make monthly payments on a mortgage. You will model the bank account,
486
your friend will write the monthly-payment class, and the two of you will work simul-
taneously. But how can your friend write his class without yours? To do this, the two
of you agree on the Java interface stated in Figure 1, which specifies, in the Java lan-
guage, the format of the yet-to-be written bank-account class. The interface states
that, whatever class is finally written to implement a BankAccountSpecification,
the class must contain two methods, deposit and withdraw, which behave as stated.
Compare Figure 1 to Table 10 of Chapter 6, which presented a similar, but informal,
specification.
A Java interface is a collection of header lines of methods for a class that is not
yet written. The interface is not itself a class—it is a listing of methods that some
class might have.
The syntax of a Java interface is simply
public interface NAME
{ METHOD_HEADER_LINES }
...
}
data types. This lets us write a constructor method that accepts (the address of) an
object that has the behavior specified in interface BankAccountSpecification and
it lets us use this object’s withdraw method in the method, makeMortgagePayment.
Class MortgagePaymentCalculator can now be compiled; the Java compiler vali-
dates that the class is using correctly the methods listed in interface BankAccountSpecification.
(That is, the methods are spelled correctly and are receiving the correct forms of ar-
guments, and the results that the methods return are used correctly.) In this way,
your friend completes the class that makes mortgage payments.
Meanwhile, you are writing the class that implements interface BankAccountSpecification;
this might look like Figure 3. This is essentially Figure 11 of Chapter 6, but notice in
the class’s header line the phrase, implements BankAccountSpecification. This tells
the Java compiler that class BankAccount can be connected to those classes that use
BankAccountSpecifications. When you compile class BankAccount, the Java com-
piler verifies, for each method named in interface BankAccountSpecification, that
class BankAccount contains a matching method. (By “matching method,” we mean
that the class’s method’s header line is the same as the header line in the interface—
the number of parameters is the same, the types of the parameters are the same, and
the result type is the same. But the names of the formal parameters need not be
488
exactly the same, and the order of the methods in the class need not be the same as
the order of the methods in the interface.)
Notice that class BankAccount has an additional method that is not mentioned
in the interface; this is acceptable.
To connect together the two classes, we write a start-up method with statements
like the following:
MortgageCalculator BankAccount
makeMortagagePayment() deposit(int amount)
withdraw(int amount): boolean
balanceOf(): int
BankAccountSpecification
deposit(int amount)
withdraw(int amount): boolean
Exercises
1. Given this interface,
which of the following classes will the Java compiler accept as correctly imple-
menting the interface? Justify your answers.
(a) public class C1 implements Convertable
{ private int x;
public C1(int a) { x = a; }
public double convert(int j)
{ return x + j; }
}
(b) public class C2 implements Convertable
{ private int x;
public C1(int a) { x = a; }
public int convert(int i)
{ return i; }
}
(c) public class C3
{ private int x;
public C3(int a) { x = a; }
public double convert(int i)
{ return (double)x; }
}
which of the following classes use the interface correctly? Justify your answers.
(a) public class Compute1
{ private Convertable convertor;
public Compute1(Convertable c)
{ convertor = c; }
public Compute3()
{ c = new Convertable(); }
3. After you have answered the previous two questions, do the following:
Execute Start.
Notice that both C1.java as well as Compute1.java use the same compiled in-
stance of Convertable.java. This arrangement is a bit awkward when two
different people write the two classes — they must share the same folder. We
will repair this difficulty when we study Java packages later in this chapter.
3. Records, regardless of their internal structure, will possess a getKey method that
returns the Record’s Key object when asked.
4. Key objects, regardless of their internal structure, will have an equals method
that compares two Keys for equality and returns true or false as the answer.
Based on these ideas, informal specifications of Record and Key were written (see
Table 3 of Chapter 8), and class Database was written to use the methods defined
in the specifications; see Figure 4 of Chapter 8.
Clearly, the types Record and Key are not meant to be specific classes; they should
be the names of two Java interfaces, so that we can compile class Database now
and decide later how to implement the two interfaces.
Figure 5 shows how to transform the informal specifications of Record and Key
from Table 3 of Chapter 8 into interfaces. These interfaces are compiled first, then
class Database from Figure 4, Chapter 8, can be compiled — please review that
Figure, now. Note how class Database refers to Record and Key in its coding. In
494
particular, Record’s keyOf method and Key’s equals method are used in crucial ways
to insert and find records in the database.
As noted in Section 8.6.5, we might use the database to hold information about
bank accounts that are identified by integer keys. Figure 5 of Chapter 8, which
defines bank accounts and integer keys, should be rewritten — Figure 6 shows these
two classes revised so that they implement the Record and Key interfaces, respectively.
Note that both classes are properly named and implement their respective interfaces.
The first class, BankAccount, keeps its key as an attribute and gives it away with
its getKeyOf method; the class knows nothing about its key’s implementation. The
second class, IntegerKey, uses an integer attribute as its internal state. Its equals
method must compare its internal integer to the integer held in its argument, c. To
do this, object c must be cast into its underlying type, IntegerKey, so that the getInt
method can be queried for c’s integer. (From the perspective of the Java compiler, an
object whose data type is Key does not necessarily possess a getInt method; the cast
is necessary to tell the compiler that c is actually an IntegerKey, which does possess
a getInt method.)
Unfortunately, we cannot avoid the cast by writing equals’s header line as
public boolean equals(IntegerKey c)
because the parameter’s data type would not match the data type of the parameter
of equals in interface Key. We must live with this clumsiness.
Now, we can build a database that holds BankAccount records; study carefully the
following, which shows how to insert and retrieve bank acccounts:
9.2. JAVA INTERFACES 495
/** getInt returns the integer value held within this key */
public int getInt() { return k; }
}
496
Database Key
insert(Record r): boolean equals(Key m): boolean
find(Key k): Record
delete(Key k): boolean
BankAccount IntegerKey
deposit(int amount)
Record getBalance(): int equals(Key m): boolean
getKey(): Key getInt(): int
getKey(): Key
Since Database’s find method returns an object that is known to be only a Record,
a cast to BankAccount is required by the Java compiler in the last statement of the
above example.
Figure 7 shows the database example’s class diagram. The diagram shows that
Database is coupled only to the two interfaces and not to the classes that implement
the interfaces. This is a clear signal that other classes of records and keys can be
used with class Database. (For example, Figure 6 of Chapter 8 shows codings of
classes of library books and catalog numbers; by recoding the two classes so that they
implement the Record and Key interfaces, the two classes can be readily used with
the database.)
Also, note how BankAccount and IntegerKey are not coupled to each other, making
it easy to “unplug” and replace both classes. Finally, by transitivity, the dotted arrows
from BankAccount to Record to Key let us infer correctly that BankAccount depends
on interface Key as well as interface Record.
Exercises
Return to Section 8.6, “Case Study: Databases,” in Chapter 8. Rework Figure 6 so
that its classes implement Record and Key.
9.3. INHERITANCE 497
9.3 Inheritance
In the previous chapters, we built many applications that paint onto a panel within
a graphics window, like this:
import java.awt.*;
import javax.swing.*;
public class MyPanel extends JPanel
{ ...
This tactic worked because, within the package javax.swing, there is a prewritten
class, class JPanel, which contains the instructions for constructing a blank graphics
panel that can be displayed on a monitor. We exploit this already written class by
• writing a new class that extends JPanel
the object we construct has all the private fields and methods within class JPanel
plus the new methods and fields within class MyPanel. This gives object p the ability
to display a panel on the display as well as paint shapes, colors, and text onto it.
This style of “connecting” to an already written class and adding new methods
is called inheritance. We say that MyPanel is a subclass of JPanel (and JPanel is a
superclass of MyPanel).
To understand inheritance further, study this small example developed from scratch:
Say that a friend has written class Person:
public class Person
{ private String name;
public Person(String n)
{ name = n; }
Pretend this class has proven popular, and many programs use it.
Next, say that you must write a new application that models persons with their
addresses. You would like to “connect” an address to a person and reuse the coding
within class Person to save you time writing your new class. You can use inheritance
to do this:
...
}
Because its title line states, extends Person, class PersonAddress inherits the fields
and methods of class Person. When we construct an object from the new class, say,
the object constructed in computer storage contains an address variable and also a
name variable. Indeed, the first statement in the constructor method for PersonAddress,
super(the_name);
invokes the constructor method within Person (the superclass), so that the person’s
name is inserted into the name field. (When this tactic is used, the super instruction
must be the first statement within the subclass’s constructor method.)
When we use the object we constructed, e.g.,
the methods from class Person can be used alongside the methods from class PersonAddress.
It is striking that we can say, x.getName(), even though there is no getName method
within class PersonAddress; the inheritance technique “connects” the methods of
Person to PersonAddress.
We use a large arrowhead in class-diagram notation to denote inheritance:
Exercises
1. Given these classes,
Which of the following statement sequences are acceptable to the Java compiler?
If a statement sequence is acceptable, what does it print when executed?
(a) System.out.println(p.getAddress());
(b) System.out.println(q.getName());
(c) System.out.println(p.sameName(p));
(d) System.out.println(q.sameName(p));
(e) System.out.println(q.same(p));
This example is studied further in the next Section.
double d = 4.5 / 2;
because a double answer can be produced by dividing the double, 4.5, by 2. Similarly,
if a method expects an argument that is a double, as in
public double inverseOf(double d)
{ return 1.0 / d; }
Thus, byte <= int, int <= long, byte <= long, etc. End footnote
In addition to the primitive data types, there are object or reference data types:
Every class C defines a reference data type, named C — its values are the objects
constructed from the class. This explains why we write declarations like
Person p = new Person("fred");
class Person defines data type Person, and variable p may hold only addresses of
objects that have data type Person.
Java interfaces and inheritance generate subtyping relationships between reference
types as well. If we write the class,
public class MyPanel extends JPanel
{ ... }
then this subtyping relationship is generated: MyPanel <= JPanel. This means that
the object, new MyPanel(), can be used in any situation where a value of data type
JPanel is expected. We have taken for granted this fact, but it proves crucial when
we construct, a new MyPanel object and insert it into a JFrame object:
// This example comes from Figure 12, Chapter 4:
import java.awt.*;
import javax.swing.*;
public class MyPanel extends JPanel
{ ... }
a2 : Record[4]
0 1 2 3
a4 null null null a4 : BankAccount
The diagram indicates that every object in storage is labelled with the name of the
class from which the object was constructed. This is the run-time data type of the
object. The diagram also illustrates that run-time data types are distinct from the
data types that appear in assignments. For example, the object at address a4 retains
its run-time data type, BankAccount, even though it was assigned into an element of
an array declared to hold Records. (Look at a2’s run-time data type.) The situation
is acceptable because of the subtyping relationship.
Consider the following statements, which build on the above:
Record r = db.find(k);
System.out.println( ((BankAccount)r).getBalance() );
The first statement extracts from the data base the record matching k, that is, a4 is
assigned to r. But we cannot say, immediately thereafter, r.getBalance(), because
variable r was declared to have data type Record, and there is no getBalance method
listed in interface Record. The problem is that the data type in the statement,
Record r = db.find(k), is distinct from the run-time data type attached to the object
that is assigned to r.
If we try to repair the situation with the assignment, BankAccount r = db.find(k),
the Java compiler complains again, because the find method was declared to return
a result of data type Record, and Record is not a subtype of BankAccount!
This is frustrating to the programmer, who knows that db is holding BankAccount
objects, but Java’s compiler and interpreter are not intelligent enough to deduce
this fact. Therefore, the programmer must write an explicit cast upon r, namely,
(BankAccount)r, to tell the Java compiler that r holds an address of an object whose
run-time type is BankAccount. Only then, can the getBalance message be sent.
If the programmer encounters a situation where she is not certain herself what is
extracted from the database, then the instanceof operation can be used to ask the
extracted record its data type, e.g.,
Record mystery_record = db.find(mystery_key);
if ( mystery_record instanceof BankAccount)
{ System.out.println( ((BankAccount)mystery_record).getBalance() ); }
else { System.out.println("unknown record type"); }
Stated precisely, the phrase, EXPRESSION instanceof TYPE, returns true exactly when
the run-time data type attached to the object computed by EXPRESSION is a subtype
of TYPE.
We can use the instanceof method to repair a small problem in the database
example in the previous section. In addition to class IntegerKey implements Key,
say that the programmer writes this new class:
/** StringKey models a key that is a string */
public class StringKey implements Key
{ private String s;
504
public StringKey(String j)
{ s = j; }
and say that she intends to construct some records that use IntegerKeys and some
that use StringKeys. This seems ill-advised, because we see a problem when an
IntegerKey object is asked to check equality against a StringKey object:
Surprisingly, the Java compiler will accept these statements as well written, and it
is only when the program executes that the problem is spotted—execution stops in the
middle of IntegerKey’s equals method at the statement,
int m = ((IntegerKey)another key).getInt(), and this exception message appears:
Despite the declaration in its header line, IntegerKey’s equals method is unprepared
to deal with all possible actual parameters whose data types are subtypes of Key!
If an application will be constructing both IntegerKeys and StringKeys, then we
should improve IntegerKey’s equals method to protect itself against alien keys. We
use the instanceof operation:
public boolean equals(Key another_key)
{ boolean answer;
// ask if another_key’s run-time data type is IntegerKey:
if ( another_key instanceof IntegerKey )
{ int m = ((IntegerKey)another_key).getInt();
answer = (id == m);
}
else // another_key is not an IntegerKey, so don’t compare:
{ answer = false; }
return answer;
}
The phrase,
if ( another_key instanceof IntegerKey )
9.4. REFERENCE TYPES, SUBTYPES, AND INSTANCEOF 505
determines the address of the object named by another key, locates the object in
storage, extracts the run-time data type stored in the object, and determines whether
that data type is a subtype of IntegerKey. If it is, true is the answer. If not, false
is the result.
Exercises
1. Given these classes,
Which of the following statement sequences are acceptable to the Java compiler?
If a statement sequence is acceptable, what does it print when executed?
(a) System.out.println(p.sameName(q));
(b) Person x = q; System.out.println(x.getName());
506
public class C
{ private int x;
public C() { x = 0; }
}
If you are interested in repairing the example, read the section, “Subclasses and
Method Overriding,” at the end of the Chapter.
(a) Insert the above coding of equals into class IntegerKey in Figure 6.
(b) Finish writing class StringKey so that it has equals method like the one
you wrote in the previous exercise. (Hint: Use the compareTo method, in
Table 5, Chapter 3, to write the lessthan method.)
(c) Write a test class that executes these statements:
Database db = new Database(4); // see Figure 3, Chapter 8
Record p = db.find(k);
BankAccount q = (BankAccount)p;
System.out.println(q.getBalance());
Key k = q.getKey();
if ( k instanceof IntegerKey )
{ System.out.println( ((IntegerKey)k).getInt() ); }
else if ( k instance of StringKey )
9.4. REFERENCE TYPES, SUBTYPES, AND INSTANCEOF 507
{ System.out.println( ((StringKey)k).getString() ); }
else { System.out.println("unknown key value"); }
public interface I
{ public int f(int i); }
public class D
{ public D() { }
public int f(int i) { return i + 1; }
}
(a) Which of the following subtyping relations hold true? C <= I; D <= I; C
<= D.
(b) Given these initializations,
I x = new C();
C y = new C();
C z = (C)x;
(d) Explain why the Java compiler complains about these statements:
508
D a = new D();
I b = a;
if ( a instanceof C )
{ System.out.println("!");
public Person(String n)
{ name = n; }
...
}
This variant of class Person has two additions from the one seen earlier:
1. In the title line, the keyword, abstract, announces that we cannot construct
Person objects, because there are missing method bodies.
2. The title line for method getAddress contains the keyword, abstract, and the
method is missing its body. The title line is terminated by a semicolon.
Because the class is abstract, we cannot construct new Person(...) objects. Instead,
we can only extend the class with its missing method body. This extension, seen
earlier, works fine:
public class PersonAddress extends Person
{ private String address;
9.5. ABSTRACT CLASSES 509
...
}
...
}
Note that the getAddress method must have the same title line as the one in the
superclass; this forces the integer to be converted into a string when it is returned.
class Dealer whose coding uses the interface to deal cards to players. (See Exercise
1, below.)
Next, we consider implementations of the CardPlayerBehavior interface. Perhaps
there are two formats of card players—computerized players and human players. (A
computerized player is an object that does card playing all by itself; a human player
is an object that helps the human user join in the play.) The two classes of player
receive cards in the same way, but when a human-player object is asked if it wants
a card, it asks the user for a decision. In contrast, a computerized-player object will
make the decision based on an algorithm — that is, the two players share the same
implementation of the receiveCard method but use different implementations of the
wantsACard method.
To accommodate the situation, we invent the abstract class, CardPlayer, that
acts as the superclass of both computerized and human card players. Figure 9 shows
abstract class CardPlayer; its subclasses, ComputerPlayer and HumanPlayer, appear
in Figure 10.
CardPlayer is labelled abstract because it is incomplete: It is missing its wantsACard
method (which it needs to implement CardPlayerBehavior). Only the header line
for wantsACard appears; it contains the keyword, abstract, and is terminated by a
semicolon. In contrast, receiveCard and another method, showCards, are written in
entirety.
The two classes in Figure 8, HumanPlayer and ComputerPlayer, extend the abstract
class, meaning the classes get the attributes and methods of a CardPlayer. The two
classes also supply their own versions of the missing wantsACard method.
At this point, there are no more missing methods, and we say that the classes
HumanPlayer and ComputerPlayer are concrete classes. Objects can be constructed
from concrete classes. (Recall that they cannot be constructed from abstract classes.)
Say that we construct two players:
/** wantsACard replies whether the player wants one more new card
* @return whether a card is wanted */
public abstract boolean wantsACard(); // method will be written later
a3 : HumanPlayer
a2 : Card[3]
public boolean wantsACard() { ... }
. . .
// from CardPlayer:
Card[] my hand ==
a4
a4 : Card[3]
int card count == 0 . . .
public void receiveCard(Card c) { ... }
public Card[] showCards() { ... }
The fields (and methods) of class CardPlayer are adjoined to those of class ComputerPlayer
to make a ComputerPlayer object. The same happens for the HumanPlayer object.
The assignment, CardPlayer someone = p, reminds us that the data types ComputerPlayer
and HumanPlayer are subtypes of CardPlayer; objects of either of the first two types
can be used in situations where a CardPlayer object is required. This means we can
send the message, boolean ask = someone.wantsACard(), because this is a behavior
expected of a CardPlayer (even though no method was written for wantsACard in the
abstract class!).
The last assignment, CardPlayerBehavior another = someone, is also acceptable,
because interface names are data types. Although it is legal to say, boolean b =
another.wantsACard(), the Java compiler will complain about another.showCards(),
because the showCards method is not listed in interface CardPlayerBehavior; a cast
would be necessary: e.g.,
if ( another instanceof CardPlayer )
{ ((CardPlayer)another).showCards(); }
The instanceof operation examines the data type stored within the object named by
another and verifies that the type is a subtype of CardPlayer.
The above example has created these subtyping relationships:
514
Dealer
HumanPlayer
CardPlayer wantsACard(): boolean
CardPlayerBehavior
wantsACard(): boolean wantsACard(): boolean
receiveCard(Card c) ComputerPlayer
receiveCard(Card c)
showCards(): Card[] wantsACard(): boolean
ComputerPlayer <=
CardPlayer <= CardPlayerBehavior
HumanPlayer <=
Exercises
1. Write class Dealer based on this specification:
private int y;
/** locationOf returns the location of the shape’s upper left corner */
public Point locationOf()
{ return upper_left_corner; }
/** widthOf returns the width of the shape, starting from its left corner */
public abstract int widthOf();
/** depthOf returns the depth of the shape, starting from its left corner */
public abstract int depthOf();
}
Now, write a concrete class, Rectangle, that extends Polygon. The constructor
method for class Rectangle can look like this:
Animal
WarmBlooded (mammal)
Feline
Lion
Tiger
. . .
Equine
Horse
Zebra
Bovine
. . .
. . .
ColdBlooded (reptile)
. . .
But the program would not create objects from the non-leaf classes, such as new
Equine(), because there are no such animals.
This example suggests that the non-leaf entries of a class hierarchy represent
typically (but not always!) abstract classes, and the leaves must be concrete classes
from which objects are constructed. The hierarchy can be converted into a collection
of classes, that extend one another, e.g.,
Form
Point
Line
Straight
Jagged
Curved
Shape
Curved
Circle
Ellipse
Polygon
Triangle
Rectangle
Exercises
1. Reread the Exercise from the section, “Abstract Classes,” that displayed sev-
eral geometric forms. Use the classes in that exercise to write classes for the
hierarchy in Figure 13. Say that class Form goes as follows:
Alter classes Point and Shape so that they fit into the hierarchy. Next, write
the classes for Line and Straight. (Remember that a line has two end points.
A straight line has no additional points, whereas jagged lines require additional
points and curves require additional information.)
an interface, the class provides, with its methods, the behavior promised by the
interface.
• Use an interface when you are connecting classes together, and you do not know
or care how the classes will be coded.
Because interfaces and abstract classes have different purposes, it is acceptable to
use both when a subassembly, as we saw in the case study with the varieties of card
players.
One reason why interfaces and subclasses are confused is because both of them
define subtype relationships. As noted earlier in the Chapter, if class C implements
I, then data type C is a subtype of I written, C <= I. Similarly, if class B extends
A, then B <= A. But remember that “subtype” is different from “subclass” — if C <=
D, it does not mean that C and D are classes and C is a subclass of D. Indeed, D and
even C might be interfaces.
The practical upshot of this transformation is C <= Object, for every class, C. In
this way, class Object defines a type Object which is the “data type of all objects.”
Some programmers exploit the situation by writing methods whose arguments use
type Object, e.g., here is a class that can hold any two objects whatsoever:
For example,
Because class Pair is written to hold and deliver objects of type Object, a cast is
needed to do any useful work with the objects returned from its methods.
Of course, primitive values like integers, booleans, and doubles are not objects,
so they cannot be used with class Pair. But it is possible to use class Integer,
class Boolean, and class Double as so-called wrappers and embed primitive values
into objects. For example, if we write
this creates an object of type Integer that holds 3. Now, we might use it with class
Pair:
In a similar way, we can “wrap” a boolean within a new Boolean and later use
the booleanValue() method and wrap a double within a new Double and use the
doubleValue() method.
Finally, since every object is built from a class that extends Object, every object
inherits from class Object a method, toString, that returns a string representation
of an object’s “identity.” For example, if we write
Integer wrapped_int = new Integer(3);
String s = "abc";
Pair p = new Pair(wrapped_int, s);
System.out.println(wrapped_int.toString());
System.out.println(s.toString());
System.out.println(p.toString());
}
The third line is the string representation of p; it displays the type of p’s object as
saved in computer storage and a coding, called a hash code, of the object’s storage
address. The toString method can prove useful for printing tracing information when
locating program errors.
9.8 Packages
When you write an application or a subassembly that consists of multiple classes,
it is best to keep the classes together in their own folder (disk directory). (If you
have been using an IDE to develop your applications, the IDE has done this for you,
under the guise of a “project name” for each application you write.) If you keep
each application’s classes in its own folder, you will better manage your continuously
growing collection of files, and other programmers will find it easier to use your files,
because they need only to remember the folder names where the application live.
A Java package is a folder of classes where the classes in the folder are marked as
belonging together. We have used several Java packages already, such as java.util,
java.awt, and javax.swing. When you write a program that requires components
from a package named P, you must insert an import P.* statement at the beginning
of your program. The import statement alerts the Java compiler to search inside
package P for the classes your program requires.
We can make our own packages. Say that we wish to group the components
written for bank accounting, listed in Figures 1 and 3, into a package named Bank.
We do the following:
9.8. PACKAGES 523
• Create a folder (called a “project package,” if you are using an IDE) with the
name Bank, and move the classes into this folder.
• Insert, as the first line of each class, the statement package Bank. For example,
Figure 1 is revised to read,
package Bank;
/** BankAccountSpecification specifies the expected behavior of a
* bank account. */
public interface BankAccountSpecification
{
... // the body of the interface remains the same
}
package Bank;
/** BankAccount manages a single bank account; as stated in its
* header line, it _implements_ the BankAccountSpecification: */
public class BankAccount implements BankAccountSpecification
{
... // the body of the class remains the same
}
• Compile each of the classes. If you are using an IDE, this step is the usual
one. If you are using the JDK, you must first close the folder, and then you
compile each class by mentioning both its folder and file names, e.g., javac
Bank\BankAccount.java
• Say that the package you assembled contained a class that has a main method,
and you wish to execute that class. To execute an application that is contained
in a package, you may proceed as usual when you use an IDE.
If you are using the JDK, then you must state both the package name and the
class name to execute. For example, pretend that the Bank package contained
a file, MortgagePaymentApplication.java, that has a main method. We compile
this file like the others, e.g., javac Bank\MortgagePaymentApplication.java.
We execute the class by typing java Bank.MortgagePaymentApplication. Note
the dot rather than the slash.
Once a collection of classes is grouped in a package, other applications can import
the package, just like you have imported javax.swing to use its graphics classes. For
example, perhaps we write a new application, class MyCheckingAccount, so that it
uses classes in package Bank. to do this, insert import Bank.* at the beginning of the
class. This makes the components in package Bank immediately available, e.g.,
524
import Bank.*;
/** MyCheckingAccount contains methods for managing my account */
public class MyCheckingAccount
{ private BankAccount my_account = new BankAccount();
private MortgagePaymentCalculator calculator
= new MortgagePaymentCalculator(my_account);
...
}
If other applications will import the Bank package, then the package must be
placed where the applications can find it. You do this by adding Bank’s directory
path to the Java compiler’s classpath.
(Begin Footnote: A classpath is a list of paths to folders that hold packages.
Whenever you compile and execute a Java program, a classpath is automatically
used to locate the needed classes. The usual classpath includes paths to the standard
packages, java.lang, java.util, etc. If you use an IDE, you can read and update the
classpath by selecting the appropriate menu item. (Usually, you “Edit” the “Project
Preferences” to see and change the classpath.) If you use the JDK, you must edit the
environment variable, CLASSPATH, to change the path. End Footnote)
For example, if you created the package, Bank, as a folder within the folder,
C:\JavaPgms, then add C:\JavaPgms to the Java compiler’s classpath. If you do not
wish to fight classpaths but wish to experiment with packages nonetheless, you can
move the Bank folder into the folder where you develop your programs. (For example,
if you do your development work in the folder, A:\MyWork\JavaExperiments, move
Bank into that folder.) This also works when one package contains classes that use
classes from another package—keep both packages within the same folder.
Exercise
Create a package from an application you have written. (Or, create the package,
Bounce, from the classes of the moving ball animation in Figures 8 through 10, Chapter
7.)
(If you use an IDE, consult the IDE’s user guide for information about javadoc.) The
javadoc program extracts the commentary from each class and creates a collection of
9.8. PACKAGES 525
HTML pages, most notably, package-summary.html, which contains links to the API
pages for each class in the package.
For example, here is the page created for the Bank package:
Exercise
Use javadoc to generate the API documentation for a package you have created.
Although we know nothing about the kinds of players in the adventure game, we
can use the interfaces to write a basic class of room for the game; see Figure 15.
A BasicRoom object remembers the address of the player that occupies it, and it is
initialized with a null address for its occupant. When a PlayerBehavior object wishes
to enter the room, it sends an enter message, enclosing its address as an argument.
The BasicRoom is not so interesting, but it is a good “building block” for designing
more elaborate rooms.
Next, we might write a class of player that remembers who it is and where it is.
Figure 16 shows the class.
Method explore of class Explorer uses the enter method of interface RoomBehavior
when it explores a room. The novelty within the method is the keyword, this. When
we write,
the keyword, this, computes to the address of this very object that sends the message
to object r. Whenever you see the keyword, this, read it as “this very object that is
sending the message.”
To understand this, consider this example:
Upon completion of the last statment, where the harpo.explore method is invoked,
9.9. CASE STUDY: AN ADVENTURE GAME 529
/** exitRoom causes the player to leave the room it occupies, if any */
public void exitRoom()
{ if ( where I am now != null )
{ where I am now.exit(this); // exit the room
where I am now = null;
}
}
we have this storage configuration, which shows that object a4 has entered room a3:
a1 : RoomBehavior[4]
RoomRoomBehavior[] ground floor == a1
0 1 2 3
Explorer harpo == a4 a2 null null a3
Explorer chico == a5
a2 : BasicRoom a3 : BasicRoom
private String secret word == "pasta" private String secret word == "swordfish"
public boolean enter(PlayerBehavior p) { ... } public boolean enter(PlayerBehavior p) { ... }
public void exit(PlayerBehavior p) { ... } public void exit(PlayerBehavior p) { ... }
public PlayerBehavior occupantOf() { ... } public PlayerBehavior occupantOf() { ... }
a4 : Explorer a5 : Explorer
Because the object named harpo lives at address a4, the invocation, harpo.explore(ground floor[3]),
computes to a4.explore(a3). Within a4’s explore method, there is the invocation,
r.enter(this), which computes to a3.enter(a4), because this computes to a4, the
address of this object in which the invocation appears.
Exercises
1. Write this class:
so that any object with PlayerBehavior can enter the room; no object that
enters the room can ever exit it; and when asked by means of its occupantOf
method, the Dungeon replies that no one is in the room.
532
2. After working the previous exercise, construct an array of size 3, so that the first
two rooms in the array are BasicRoom objects named "kitchen" and "lounge",
respectively, and the third object is a Dungeon. Then, construct
and write a for-loop that makes harpo systematically try to enter and then exit
each room in the array.
3. A close examination of Figures 15 and 16 shows us that it is possible for one
Explorer object to occupy simultaneously multiple BasicRooms. Write a se-
quence of Java statements that makes this happen. (Hint: in the example at
the end of the section, make both rooms use the same password.)
How can we make a BasicRoom object “smart” enough so that it refuses to let
a player be its occupant when the player currently occupies another room? To
do this, add this method to interface PlayerBehavior:
}
return answer;
}
}
In the general case, a class can implement an arbitrary number of interfaces; the
interfaces are listed in the implements clause, separated by commas, within the class’s
header line.
The coding of class Vault possesses a major flaw: It copies the codings of methods
already present in class BasicRoom. For this reason, we should rewrite the class so
that it uses inheritance to use BasicRoom’s methods. Figure 19 shows the corrected
coding.
The header line of class VaultRoom indicates that VaultRoom extends BasicRoom;
this makes VaultRoom a subclass of the superclass BasicRoom. The class also imple-
9.9. CASE STUDY: AN ADVENTURE GAME 535
ments the interface, Treasury, because it has a yieldTreasure method that matches
the one in that interface. (Indeed, the class also implements interface RoomBehavior,
because it extends BasicRoom and BasicRoom implements RoomBehavior.)
Within VaultRoom’s constructor method, we see super(name, password), which
invokes the constructor method of the superclass, ensuring that the private fields
within BasicRoom are initialized. (Recall that when the super-constructor is invoked,
it must be invoked as the first statement in the subclass’s constructor.)
Within the yieldTreasure method, we see an invocation of the occupantOf method
of BasicRoom:
if ( p == occupantOf() )
The invocation asks this object to execute its own occupantOf method to learn the
player that occupies it. The invocation is required because the field occupant is
declared as private to class BasicRoom, so subclass VaultRoom cannot reference it
directly.
As the comment next to the invocation states, we can also invoke this particular
method by
if ( p == super.occupantOf() )
This format asserts that the occupantOf method must be located in a superclass part
of this object. (If the method is not found in a superclass of VaultRoom, the Java
compiler will announce an error.) Finally, it is also acceptable to state
if ( p == this.occupantOf() )
which is equivalent to the first invocation and documents that the invocation message
is sent to this very object.
Using the above classes, say that we construct one BasicRoom object and one
VaultRoom object:
BasicRoom a = new BasicRoom("The Lounge", "Hello");
a3 : VaultRoom
// these fields and methods are from class VaultRoom:
private TreasureProperty valuable ==| a2 |
public TreasureProperty yieldTreasure(PlayerBehavior p) { ... }
// these fields and method are from class BasicRoom:
The objects in storage show us that the structure of class BasicRoom is used to build
the objects at addresses a1 and a3. In particular, the VaultRoom object at a3 inherited
the BasicRoom structure plus its own—all the methods of a BasicRoom object can be
used with a VaultRoom object.
Because VaultRoom extends BasicRoom, the resulting data types are related by
subtyping—data type VaultRoom is a subtype of data type BasicRoom. Further, since
BasicRoom is a subtype of RoomBehavior, then by transitivity, VaultRoom is a subtype
of RoomBehavior as well. Here is a drawing of the situation:
<= BasicRoom <= RoomBehavior
VaultRoom
<= Treasury
The Java compiler uses these subtyping relationships to verify that VaultRoom objects
are sent appropriate messages.
Finally, the diagram for the classes defined in this and the previous sections ap-
pears in Figure 20. The diagram shows how interfaces act as the connection points
9.9. CASE STUDY: AN ADVENTURE GAME 537
RoomBehavior
BasicRoom
Treasury
PlayerBehavior Explorer
VaultRoom
TreasureProperty
for the concrete classes; it shows that the classes BasicRoom and VaultRoom form a
subassembly that is kept separate from Explorer. We also see that a VaultRoom has
RoomBehavior and depends on PlayerBehavior. Indeed, as we study the diagram fur-
ther, we might conclude that an Explorer might be profitably extended by methods
that fetch treasures from the rooms explored; this is left for the Exercises.
Exercises
1. Write a class that implements TreasureProperty:
and construct a Vault object with the name, "lounge" and the password,
"swordfish", to hold an object constructed from class GoldCoin.
2. Modify the method, explore, in Figure 10 so that once the player successfully
enters the room, it tries to extract the treasure from it. Note that not all rooms
will implement interface Treasury, however. (Hint: use instanceof.) Then,
construct an explorer named "Harpo Marx" that explores the "lounge".
3. Here is an interface:
/** TreasureHunter explores rooms and saves all the treasures it extracts
* from the rooms. */
public class TreasureHunter extends Explorer implements TreasureHunterBehavior
4. The TreasureHunter defined in the previous exercise does not automatically try
to take a treasure each time it successfully enter a room. (The TreasureHunter
must be sent an enter message followed by a takeTreasure message.)
We can create a treasure hunter that has more aggressive behavior: First write
this method:
/** explore attempts to enter a room and explore it. If the room is
* successfully entered, then an attempt is made to take the treasure
* from the room and keep it (if the room indeed holds a treasure).
* @param r - the room that will be explored
* @return whether the room was successfully entered */
public boolean explore(RoomBehavior r)
Now, insert your coding of the new explore into this class:
The new explore method of the RuthlessHunter executes instead of the old,
same-named method in the superclass, Explorer. We say that the new explore
method overrides the old one.
Construct this object:
9.10. SUMMARY 539
9.10 Summary
This chapter has presented four constructions that can improve the design and im-
plementation of programs: the interface, subclass, abstract class, and package.
540
New Constructions
Here are examples of the constructions introduced in this chapter:
• package:
9.10. SUMMARY 541
package Bank;
/** BankAccount manages a single bank account; as stated in its
* header line, it _implements_ the BankAccountSpecification: */
public class BankAccount implements BankAccountSpecification
{ ... }
• instanceof operation:
• super:
New Terminology
• interface: a specification of the behaviors (methods) expected of a class. A Java
interface is a named collection of header lines of public methods.
• inheritance: writing a class that includes in itself the fields and methods of an
existing class and adding new ones. The keyword, extends, precedes the name
of the existing class that will be included in the new one.
• abstract class: a class that lacks some of its methods; the missing methods are
noted by a header line that contains the keyword, abstract.
• package: a collection of classes that are grouped together in a folder and labelled
with a common name.
• run-time data type: the data type that is saved inside an object when the object
is constructed in computer storage. The saved data type is the name of the class
from which the object was constructed.
• instanceof: a Java operation that examines the run-time data type of an object;
ob instanceof C returns true exactly when the run-time data type of ob is a
subtype of C.
• super: the statement that invokes the constructor method of a superclass within
the constructor method of its subclass.
• abstract method: a method without its body found in an abstract class. The
method’s body is supplied in a subclass of the abstract class.
• wrapper class: a class whose primary purpose is to hold a single primitive value,
thereby allowing the primitive value to be used as if it were an object, e.g., new
Integer(2) makes 2 into an object.
Points to Remember
• Interfaces are used to specify the behavior of a class that to be written; other
classes can refer to the interface in their codings. Interfaces are also used to
specify “connection points” to which subassemblies of an application may con-
nect.
• A Java interface defines a data type name, and when a class implements the
interface, the class’s data type name is a subtype of the interface name.
9.11. PROGRAMMING PROJECTS 543
• Just as you have written graphics-window classes that extend JPanel, you may
write your own class C and extend it by class D extends C. This makes D a
subclass of C, meaning that all of C’s structure is included within the structure
of a D-constructed object. Further, data type D is a subtype of C.
• An abstract class is used when you wish to specify a partially written class,
which has codings (bodies) for some of its methods. The classes that extend
the abstract class use the methods coded in the abstract class.
• Use packages to group together collections of classes that are likely to be used
together.
3. Say that a company that sells chocolates, and the company requires a database
that remembers its salespeople and how many chocolates each has sold. The
database must also remember the managers of the salespeople and how well
each managers’ people have done at selling chocolates. Design and implement
the database; use interfaces where appropriate.
4. Design and build a telephone directory database program. The program must
manage a collection of patrons and their telephone numbers. Patrons are either
individuals or businesses; telephone numbers are either listed or unlisted. Input
to the program is a sequence of queries. Queries can take these forms:
• a telephone number (The program prints the patron that owns that num-
ber.)
Remember that a patron might own more than one telephone number and that
unlisted numbers cannot be included in the response to a query that supplies a
patron’s name.
5. Say that a bank deals with two forms of customers: regular and preferred. (A
preferred customer is charged no service charge on her accounts.) The bank
offers three kinds of accounts: checking, savings, and checking-with-interest.
(See the Exercises for the “Abstract Classes” section for details about the three
forms of accounts.)
Design and implement two databases: one for the bank’s accounts and one
for the bank’s customers. Remember that a customer might own more than
one form of bank account, and every bank account has an owner, which is a
customer.
The optional sections that follow examine the consequences that arise when the same
name is given to more than one method. There are two variants of this phenomenon—
overriding and overloading
The paintComponent method contains the instructions for painting on the panel.
There is an important detail which was skipper earlier: There already is a method
named paintComponent inside class JPanel, but the method does nothing! Nonethe-
less, the Java language lets us write a new, second coding of paintComponent, which
will be used with new MyPanel objects. The new coding of paintComponent overrides
the useless version.
(Begin Footnote: Perhaps class JPanel should have been written as an abstract
class, where its paintComponent method should have been written as public abstract
void paintComponent(Graphics g). But class JPanel is written so that its “default”
paintComponent lets a beginner easily construct a blank panel. End Footnote)
When one writes a class, C2, that extends a class, C1, and class C2 contains a
method whose name and formal parameters are exactly the same one in class C1,
then we say that the new method overrides the same-named method in C1. Here is a
small example: Given class C:
public class C
{ public C() { }
The add method in D overrides the one in C. The intent is: new C() objects use the
add method coded in class C, and new D() objects use the add method in class D.
If we write these statements,
C a = new C();
D b = new D();
System.out.println(b.add(3, 2));
System.out.println(a.add(3, 2));
BasicAccount b == a1 a1 : BasicAccount
int balance == 100
CheckingAccount c == a2
public void deposit(int amount) {...}
public int balanceOf() {...}
AccountWithInterest i == a3
a2 : CheckingAccount
public boolean withdraw(int amount)
// from BasicAccount:
int balance ==
0
public void deposit(int amount) {...}
public int balanceOf() {...}
a3 : AccountWithInterest
Each of its three objects has its own combination of fields and methods and behaves
uniquely to specific messages. At this point, if we send the message, i.withdraw(500),
the object at a3 uses its “newest” withdraw method, the one from class AccountWithInterest,
to make the withdrawal. This method itself invokes the older withdraw method to
complete the transaction. (The keyword, super, forces the older method to be used.)
550
the compiler
1. determines the data type of RECEIVER, which will be a class name or an interface
name, C0. (Note: Since Chapter 5, we have learned that RECEIVER can be the
keyword, super. In this case, the data type is the superclass of the class where
the method invocation appears. There is one more keyword, this, which can
appear as a RECEIVER. The data type of this is the class where the method
invocation appears.)
2. selects the best matching method for NAME0. This will be a method of the form
that is found within class or interface C0 (or, if not in C0, then in C0’s superclass,
C1, or then in C1’s superclass, C2, and so on—see Chapter 5 for the algorithm).
• If it is super Ck, select the public method that came from class Ck whose
header line has this form:
public ... NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)
• If it is public, look at the data type attached to object a; say that it is C0.
Search the public methods starting with the ones that came from class
C0, for a method whose header line has the form,
552
If the method is found in class C0, select it. Otherwise, search the public
methods that came from C0’s superclass; repeat the search until the method
is found.
C1 c1 = new C1(3);
c1.print();
it is easy to guess correctly that value is C1: 3 is printed; print invokes the
stringVal method in C1. Indeed, when the Java compiler type checked class C1,
it came to the conclusion that print invokes stringVal in C1.
But if we write,
C2 c2 = new C2(4,5);
c2.print();
this code’s execution produces value is C1: 4 C2: 5. This occurs because print
is executed within a C2 object, and therefore uses the object’s “newest” version of
stringVal, which is the C2 version. This is not what was selected by the Java compiler,
but the execution works properly because the data typing of the newer version of
stringVal is identical to the data typing of the older version. This means the Java
compiler’s type checking efforts are not harmed.
We can see the rationale behind this execution result by drawing the object cre-
ated by the above declaration. The drawing displays the annotations that the Java
9.12. BEYOND THE BASICS 553
public C1(int x)
{ i = x; }
The annotations, private Ci and super Ci, select specific methods for execution;
there is no re-search. But a method invocation that is labelled public requires a
search of all the public methods to find the newest version of the invoked method
name.
For the example, the compiler labels the starting invocation as c2.print() public.
First, c2 computes to address a2, and the public methods of a2 are searched for the
newest version of print. The only version, the one from class C1, is located, so the
statement,
System.out.println( "value is " + (stringVal() public) );
executes. This sends the message, stringVal() public, which is directed again to a2,
because there is no RECEIVER on the method name.
Another search proceeds, this time for the newest version of stringVal. This
time, the version from class C2 is selected. (This is a different result than the Java
compiler’s search, and certainly the author of class C1 might be a bit surprised as
well!)
The selected method executes this statement:
return (super.stringVal() super C1) + (f(j: int) private C2);
The first message, super.stringVal() super C1, is directed again to a2, which is
forced to use the stringVal method from class C1, because of the SUFFIX, super C1.
The body of C1’s stringVal states,
return (f(i: int) private C1);
which forces the private method named f from class C1 to execute. Therefore, the
string, "C1: 4", is returned as the answer.
Similarly, the invocation, f(j: int) private C2 forces class C2’s f method to
execute, producing "C2: 5". These strings are combined and produce the printed
answer, value is C1: 4 C2: 5.
9.12. BEYOND THE BASICS 555
In summary, the Java compiler selects precisely and correctly the private and
“super” methods used by invocations. But it is misled about invocations of public
methods—the method the compiler selects might be overridden by a newer version
(fortunately, with the same data typing!) when objects are created at execution.
Whether this is a positive feature of the Java language is subject to debate, but
in any case, you must be well aware of these execution behaviors when you write
overriding methods.
Now, the Java compiler will not allow any other class to use the class as a superclass,
that is, public class D extends C is disallowed. One consequence is that no method
in C can be overridden.
When a method is labelled final, then it cannot be overridden. For example, if
the override of C1’s stringVal method in Figure 23 is upsetting, we can modify C1 to
read
public class C1
{ private int i;
The two methods differ in the number of parameters they require; the method name,
showTransaction, is overloaded.
A method name is said to be overloaded when there are two methods that are
labelled with the same name but have different quantities or data types of formal
parameters; the two same-named methods appear in the same class (or one appears
in a class and another in a superclass).
The overloading technique is promoted as a “memory aid” for a programmer: If
there is a collection of methods that more-or-less act the “same way” but differ in the
parameters they require to “act,” then the methods can be overloaded. The above
example is typical—both variants of showTransaction display a transaction result,
9.12. BEYOND THE BASICS 557
but the first handles transactions that include text and numerical values, and the
second handles text-only transactions.
Overloading often appears in a class’s constructor methods:
public BankAccount()
{ balance = 0; }
...
}
Both constructor methods are initializing the bank account, but they differ in the
parameters they use to do so. Now, a bank-account object can be created two ways—
by new BankAccount() or by new BankAccount(1000), say.
Another form of overloading is based on parameter data type. The following
methods overload the name, displayValue, by distinguishing between the types of
the methods’ parameter:
It is easy to select the proper method based on the actual parameter supplied with the
method invocation, e.g., displayValue("Hello") or displayValue(new BankAccount(300)).
But some invocations do not match any of the methods, e.g., displayValue(2.5)—the
Java compiler will report an error in this situation.
Perhaps you have deduced that System.out’s println is itself overloaded. Indeed,
there are ten different methods! These include
The behavior of f(3) and f(3.5) are not too difficult to guess correctly. What about
long x = 3000000000;
... f(x) ...
That is, when the actual parameter is a long integer? There is no variant of f for
data type long, but since long is a subtype of double (and not of int), the parameter
is cast into a double, and the second variant is chosen; "fractional" is printed and
false is returned.
Figure 24 presents a more perplexing example. In the example, if we declare
public OneInt(int x) { i = x; }
These seem like reasonable definitions of equality checks for the two forms of objects.
But consider this statement, which uses objects s and t:
System.out.println(equals(s, t));
This statement prints true! There is no equals method when one actual parameter
has type OneInt and the other has type TwoInt, but since TwoInt is a subtype of
OneInt, actual parameter t is cast to OneInt, and the first method is selected.
If you are uncomfortable with the above answer, you can write these two additional
methods:
public boolean equals(OneInt x, TwoInt y)
{ return false; }
Now, all combinations of data types are listed, and equals(s, t) returns false. But
this example,
OneInt u = t;
System.out.println(equals(u, t));
returns false as well, even though variables u and t both hold a2 as their value!
The precise reasons why these behaviors unfold as they do are explained in the
next section. But the moral is, if at all possible, avoid overloading a method name by
methods that are distinguished by a formal parameter whose respective data types are
related by subtyping. That is,
• it is acceptable to overload a method name based on quantity of parameters
(cf., the showTransaction example earlier)
public AbsInt(int x) { i = x; }
Since it is illegal to create AbsInt objects, class OneInt is essential. And, no longer
is there a subtyping relationship between OneInt and TwoInt, which was at the root
of our earlier difficulties.
Say that the compiler has calculated that C0 is the data type of the RECEIVER and say
that each EXPRESSIONi has data type Ti, for i in 1..n.
What method will be invoked by this invocation? If the invocation appears
within class C0 also, then the best matching method is the method the compiler
finds by searching(all public and private methods of class C0); Otherwise, the
best matching method is the method found by searching(all public methods of
class C0).
The algorithm for searching(some of the methods of class C0) is defined as fol-
lows:
Within some of the methods of class C0, find the method(s) whose header line
has the form,
such that each Ti is a subtype of TYPEi, for all i in the range of 1..n.
• If there are two or more variants of NAME0 in C0 that fit the criterion stated
above (that is, NAME0 is overloaded), then the variant whose formal parame-
ter types most closely match the types of the actual parameters is selected.
If none of the variants most closely match, then the invocation is said to
be ambiguous and is not well typed; the search fails. (The definition of
“most closely match” is given below.)
• If there is no method definition for NAME0 that is found, and if class C0
extends C1, then the best matching method comes from searching(all
public methods of class C1). But if class C0 extends no other class,
there is no best matching method, and the search fails.
Here is the definition of “most closely match”: Consider a single actual parameter,
E, with data type, T and the data types, T1, T2..., Tn, such that T is a subtype of each
of T1, T1, ... Tn. We say that one of the data types, Tk, most closely matches T if Tk
is itself a subtype of all of T1, T2, ..., Tn. (Tk is considered to be a subtype of itself by
default.)
Next, take this definition and apply it to all n of the actual parameters of a method
invocation: For a variant of NAME0 to mostly closely match a method invocation, the
data type of each of its formal parameters must most closely match the type of the
corresponding actual parameter of the method invocation.
If we return to the example in Figure 18 and reconsider this situation:
we see that first and third methods named equal match the invocation, equals(s, t),
because the data types of the actual parameters are OneInt and TwoInt, respectively.
When we compare the data types of the formal parameters of the two methods, we
find that the third variant most closely matches.
Finally, we must note that the Java compiler in fact implements a more restrictive
version of best matching method, prohibiting some examples of overloading that
extend across super- and subclasses. For example, the Java compiler will refuse to
calculate a best matching method for this example:
9.12. BEYOND THE BASICS 563
public class A
{ public A() { }
because the above definition of “best matching method” would select the version of
f in B as the best match to ob.f(3), even though there is a more appropriate version
in the superclass, A.
Chapter 10
10.2 Events
10.5.3 A Button-Controller
10.13 Summary
Just as the connection points between program components are called interfaces,
the “connection point” between a program and its human user is called its “user
interface.” A program that uses visual aids—buttons, scroll bars, menus, etc.—to
help a user enter input and read output is called a graphical user interface (“GUI”
for short, pronounced “goo-ee”).
In this chapter, we learn the following:
• how to employ Java’s AWT/Swing framework to design graphical user interfaces
• how to write programs whose controllers are distributed and event driven. That
is, a program has multiple controllers, and a controller executes when it is started
by a user action (event), e.g., a button press.
• how to use the observer design pattern to streamline the collaborations between
the components of a program that uses a GUI.
message (either indirectly by the model or directly by the controller) to display the
results. The view queries the model, getting information about the model’s state and
presenting this information to the user. Figure 1 depicts the situation.
Another important aspect about the MVC-architecture is that it can be composed,
that is, one component—say, the view—might be built from smaller components—
say, buttons, text fields, and menus—that are themselves constructed with their own
little MVC-architectures. For example, consider a text field, which is a component
within a view where a user can type text. The text field has its own little appearance
or view. (Typically, it is an area that displays the letters the user typed while the
mouse was positioned over the area.) When a user types a letter into the text field,
this activates the text field’s controller, which transfers the letter into the text field’s
internal model. Then, the text field’s view is told to refresh itself by asking the model
for all the letters that have been typed; the view displays the letters.
Of course, it would be a nightmare if we must design from scratch the little MVC
architectures for buttons, menus, text fields, and the other GUI components! For
this reason, we use a framework that contains prebuilt components and provides
interfaces for connecting them. In Java, the framework for building GUIs and con-
necting them to controllers and models is called AWT/Swing. (The AWT part is the
java.awt package; the Swing part is the javax.swing package. “AWT” stands for
10.2. EVENTS 567
“Abstract Window Toolkit.”) The bulk of this chapter introduces a useful subset of
AWT/Swing.
Exercise
Consider a television set. What are its controllers? model? view? Answer the same
questions for a calculator. How can a television have multiple views? How can a
calculator have multiple views?
10.2 Events
In the programs we built in previous chapters, the program’s controller was “in
control”—it controlled the sequence of steps the user took to enter input, it controlled
the computation that followed, and it controlled the production of the program’s out-
put. When one employs a GUI, this changes—the program’s user decides when to
enter input, and the controllers must react accordingly. The moving of the mouse,
the pushing of a button, the typing of text, and the selection of a menu item are all
forms of input data, and the controllers must be prepared to calculate output from
these forms of input. The new forms of input are called events, and the style of
programming used to process events is called event-driven programming.
Event-driven programming is more complex than the programming style we em-
ployed in earlier chapters: When a program receives events as input, a coherent “unit”
of input might consist of multiple events (e.g., a mouse movement to a menu, a menu
selection, text entry, mouse movement to a button, and a button push); the program
that receives this sequence of events must be written so that it can
• process each individual event correctly—this is called handling the event. The
controller that handles the event is sometimes called the event handler or event
listener.
• accumulate information from handling the sequence of events and generate out-
put. Typically, the controllers that handle the events save information about
them in model objects.
Further, the user of the program might generate events in unexpected or incorrect
orders, and event handlers must be prepared to handle unwelcome events.
The previous examples should also make clear that event-driven programs use
multiple controllers (event handlers), so there is no longer one controller that oversees
the execution of the entire program. Instead, execution is distributed across multiple
controllers that are activated at the whim of a user. For this reason, an event-driven
program is a bit like a crew of night-duty telephone operators who are repeatedly
awakened by telephone calls (events) and must process each call in a way that keeps
the telephone station operating smoothly through the night. The telephone operators
568
cannot predict when the telephone will ring and what each call’s request might be;
the operators must react to the evening’s events rather than dictate what they might
be.
To assist an event driven program, a programmer must design the program’s GUI
so that
• sequences of events are organized into natural units for processing
• it is difficult or impossible for the user to generate a truly nonsensical sequence
of events
To help with the first objective, we will usually design our GUIs so that computa-
tion occurs only after a sequence of events terminates with a button push or a menu
selection. (In Java, button pushes and menu selections are called action events, and
their event handlers are called action listeners.) We will let mouse-movement and
text-entry events be handled by the default event handlers that are already built into
the components of the Java AWT/Swing framework.
To assist with the second objective, we will design our GUIs so that the action
events that can be generated from a window can occur in any order at all. If a program
must enforce that one action event must occur before another, we will program the
GUI so that the first action event causes a secondary window to appear from which
the second action event can be generated.
Because we let the AWT/Swing components handle mouse movement events and
text entry events, we need not write extra controllers to monitor the position of the
mouse on the display or to monitor every key press into a text field—we use the
code that is already in place in the default codings of windows and text fields. This
should make clear why a framework is so useful for building graphical user interfaces:
Many intelligent classes are available for immediate use, and we need worry only
about programming controllers for those events (here, action events) that we choose
to handle in a customized way.
executes. A dialog is a “temporary” window that can appear and disappear while the
program is executing.
Examples of components that one finds within panels and frames are
• a label, which is text that the user can read but cannot alter
Figure 2 shows a frame that contains a label, a text component, a list, and three
buttons. Although it is not readily apparent, the text component and list are em-
bedded in a panel, which was inserted in the middle of the frame, and the label and
button live in their own panels in the top and bottom regions of the frame.
Figure 3 displays an example dialog, which might have appeared because the user
entered text and pushed a button in the frame behind. The dialog contains a label
and a button; when the button is pushed, the dialog disappears from the screen.
A frame can hold a menu bar, which holds one or more menus. Each menu contains
menu items, which can be selected, triggering an action event. Figure 4 shows a frame
with a menu bar that holds two menus, where the second menu is open and displays
four menu items, one of which is itself a menu:
When a container holds multiple components, the components can be formatted
with the assistance of a layout manager. Three basic forms of layout are
• flow layout: the components are arranged in a linear order, like the words in a
line of text
570
• grid layout: components are arranged as equally-sized items in rows and columns,
like the entries of a matrix or grid
In Figure 2, the frame is organized with a 3-by-1 grid layout, where the second element
of the grid is a panel containing a text component and list. The panel uses flow layout
to arrange its two components. We will see an example of border layout momentarily.
The AWT/Swing framework contains dozens of classes, each of which owns dozens
of methods. It is overwhelming to learn the entire framework, so we will master a
manageably sized subset of it. Figure 5 displays the parts of AWT/Swing we will use;
aside from reading the names of the various classes, do not study the Figure at this
point—use it as a reference as you progress through the chapter.
Since the AWT/Framework was developed in several stages by the Java designers,
the elegant hierarchy of component-container-panel-window-frame is obscured in the
final product in Figure 5.
The classes in Figure 5 require other classes that define points, type fonts, images,
layouts, and events. Figure 6 lists these extra classes. Again, study the Figure as you
progress through the chapter.
As all frameworks must do, AWT/Swing uses a variety of interfaces for connecting
view components to controllers. (Review Chapter 9 for uses of Java interfaces; an
introductory explanation appears later in this chapter, also.) The interfaces within
AWT/Swing that we employ appear in Figure 7.
Figures 5 through 7 list the constructs we use in this chapter, and you should use
the Figures as a road map through the examples that follow.
Exercises
1. List all the methods owned by a JFrame. (Remember that the JFrame inherits
the methods of its superclasses.) Compare your answer to Table 21, Chapter 4.
The first example is a frame that holds the label, Press This, and a button named
OK. These two components must be inserted into the frame, and we must indicate the
form of layout. We use flow layout, which places the two components next to each
other. Here is the result,
• Container c = getContentPane() asks the frame to extract (the address of) its
content pane and assign it to c. Many examples in earlier chapters invoked
getContentPane, and now it is time to understand the activity.
For the moment, pretend that a JFrame object is in fact a real, physical, glass
window that is assembled from several layers of glass. The frame’s topmost
574
layer is called the glass pane, and it is possible (but not recommended) to paint
on it. When we insert components like panels, labels, and buttons into the
frame, the components are inserted into an inner layer, called the content pane.
(There are additional layers of “glass,” but we will not deal with them in this
chapter.)
The statement, Container c = getContentPane(), fetches the content pane. We
could also write the statement as
Container c = this.getContentPane();
but from this point onwards, we take advantage of Java’s convention: a message
that an object sends to itself need not be prefixed by this.
• c.add(label) uses the content pane’s add method to add label; c.add(button)
adds button also.
10.4. SIMPLE WINDOWS: LABELS AND BUTTONS 577
• Finally, the setTitle, setSize, and setVisible messages are the standard ones.
Again, we omit the this pronoun as the receiver.
The names, label and button, are not crucial to the example, and we might revise
the above statements to read,
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(new JLabel("Press This:"));
c.add(new JButton("OK"));
because we have painted on the frame’s topmost, “glass,” pane, covering the compo-
nents we inserted into the content pane.
Although we should not paint directly onto a frame, we can set the background
and foreground colors of the content pane and the components we insert into it. For
example, to make the content pane’s background yellow, we state,
Container c = getContentPane();
c.setBackground(Color.yellow);
We can color red the foreground text on both label and button by saying
label.setForeground(Color.red);
button.setForeground(Color.red);
This works because every component has a “background” and “foreground” that can
be colored.
Buttons usually have text displayed on their faces, but it is possible to display
images, called icons, as well. If you have an image formatted as a gif or jpg file, you
can place the image on the face of the button as follows:
which displays
That is, the image file, mypicture.gif, is used to construct an ImageIcon object,
which itself is used to construct the button. A button can display both an image and
text:
Once the frame and its button appear on the display, you can move the mouse
over the frame’s button and push it to your heart’s content. But the program takes
no action when the button is pushed, because the program has only a view object
but no model and no controller. We write these next.
Exercises
1. Create a frame with three buttons whose labels state, Zero, One, and Two. Create
the frame with different sizes, e.g., setSize(300, 150), setSize(50, 300), and
pack().
2. Color the frame’s background white, color each button’s background a different
color, and color each button’s text (foreground) black.
3. Replace the three buttons with one label that states, Zero One Two. Color the
background and foreground of the label.
10.5. HANDLING AN EVENT 579
That is, the class’s header line asserts implements ActionListener, and the class
contains an actionPerformed method. As we learned in Chapter 9, we use a Java
interface to name the methods required of a class. In AWT/Swing, there is a
prewritten interface, named ActionListener, which is found in the java.awt.event
package. The interface was listed in Figure 7; once again, it looks like this:
The interface states that an action listener must have a method named actionPerformed.
AWT/Swing requires that a button’s action listener must be an object that is con-
structed from a class that implements ActionListener. We now study three standard
ways of writing action listeners.
Since the view is also the controller, class Frame2a states in its header line that
it implements ActionListener. And indeed, method actionPerformed appears at the
end of the class. (We study the contents of actionPerformed momentarily.) Also, the
statement, import java.awt.event.*, appears at the beginning of the class because
the java.awt.event package contains the ActionListener interface.
Now, how do we connect the button to its action listener, the view object? The
answer appears within the view’s constructor method:
JButton button = new JButton("OK");
cp.add(button);
button.addActionListener(this);
The first two statements create the button and add it to the view, like before. The
third statement connects button to its action listener—every button has a method,
named addActionListener, which is invoked to connect a button to its action listener.
Here, the action-listener object is this very object—the view object, which displays
the button! From this point onwards, every push of button causes the view object’s
actionPerformed method to execute.
Frame2a’s actionPerformed method handles a button push by making the counter
increment and by resetting the text of the label to display the counter’s new value.
The method receives a parameter that contains technical information about the but-
ton push; we will not use the parameter at this time.
Here is a slightly detailed explanation of what happens when the program in
Figure 10 is started and its button is pushed. When Example2a is started:
1. The main method creates objects for the model and the view/controller. Frame2a’s
constructor method creates a label and a button. The button is sent an addActionListener
message that tells the button that its action events will be handled by the
Frame2a object.
2. The view appears on the display, and the program awaits events.
When the user clicks on the frame’s OK button:
1. The computer’s operating system detects the event, and tells AWT/Swing
about it. AWT/Swing determines the event source (the button), and creates
an ActionEvent object that holds precise information about the event. The
ActionEvent object is sent as the actual parameter of an actionPerformed mes-
sage to the event source’s action listener, which is the Frame2a object.
2. The message arrives at actionPerformed in Frame2a. The actionPerformed
method sends an increment message to the counter object and a setText mes-
sage to the label.
3. When actionPerformed’s execution concludes, the computer automatically re-
freshes the view on the display; this displays the label’s new value.
10.5. HANDLING AN EVENT 583
The explanation should make clear that computation occurs when events trigger exe-
cution of event listeners. This is why computation is “event driven.”
which constructs a new CountController object (see Figure 12), gives it the addresses
of the model object and view object (this object), and attaches the controller to the
button as the button’s action listener.
Because the controller is separate from the view, the former will need a way to
tell the latter when it is time to display a new value for the count. For doing this,
the view class has a method, update, which resets the text of the label with the latest
value of the count.
We have simple coding of the controller, class CountController, in Figure 12.
The controller implements ActionListener and it holds the actionPerformed method
that is invoked when the OK button is pushed. This controller resembles the ones we
saw in previous chapters, because its job is to tell the model object to compute results
and tell the view to display the results.
10.5.3 A Button-Controller
The third solution to the example merges the JButton object with its controller. This
follows the philosophy that, to the user, the button is the controller, and pushing the
button activates its methods. The button-controller we write appears in Figure 13.
CountButton is a “customized” JButton, hence it extends JButton. Its constructor
method starts work by invoking the constructor in JButton—this is what super(my label)
does—and creates the underlying JButton and attaches my label to the button’s face.
But the constructor for CountButton does more: The crucial statement is, addActionListener(this),
which tells the button that its action listener is this very same object. Thus, the
class implements ActionListener.
584
The view that uses the button-controller is in Figure 14, and it is the simplest of
the three versions we have studied.
Figure 15 summarizes the architecture that we have assembled for the third variant
of the counter application.
The interface, written in italics, serves as the connection point between the button-
controller and the AWT/Swing framework.
We can revise the application in Figure 15 so that it it presents two buttons, an OK
button that changes the count, and an Exit button that terminates the application:
The result holds interest because there are now two controllers, one for each action
event. The controller for terminating the program appears in Figure 16. (Recall that
System.exit(0) terminates an application.)
Next, Figure 17 defines the view, class Frame3, as a quick extension of class
Frame2c from Figure 14. Although an abstract class, like those in Chapter 9, might
be a better solution for organizing the previous and revised views, we use the existing
586
AWT/Swing classes
JButton ActionListener that detect events
actionPerformed(ActionEvent e)
Counter
increment()
countOf()
approach if only to show that components can be added to a frame’s content pane
incrementally. (See Frame3’s constructor method.)
Finally, users of Windows-style operating systems have the habit of terminating
applications by clicking the “X” button that appears at a window’s upper right cor-
ner. The buttons at the upper right corner are monitored through the AWT/Swing
WindowListener interface. (See Figure 7 for the interface.) Since it does not deserve
a long explanation, we merely note that the changes presented in Figure 18 will make
a mouse click on the “X” button terminate a program.
Exercises
1. Create an interface for an application whose model possesses two Counter ob-
jects. Create two buttons, each of which increments one of the counters when
pushed. Use labels to display the values of the two counters.
590
4. Create a GUI with a red button, a yellow button, and a blue button. The
frame’s background turns into the color of the button last pushed.
The north and south regions take precedence in layout, followed by the east and
west regions. Any space left in the middle becomes the center region. The layout is
delicate in the sense that an undersized window may cause components in a region
to be partially hidden.
Figure 20 presents the view for the GUI in Figure 19.
In Frame4’s constructor, the content pane is told to use border layout:
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
Next, components are added to the regions of the content pane, e.g.,
cp.add(drawing, BorderLayout.CENTER);
In addition to the regions used in the Figure, one can also use BorderLayout.EAST
and BorderLayout.WEST.
Panels are simply created and inserted into the frame, e.g.,
592
/** Constructor Frame4 creates a frame with label, drawing, and 2 buttons
* @param c - the model object, a counter
* @param panel - a panel that displays a drawing */
public Frame4(Counter c, JPanel panel)
{ count = c;
drawing = panel;
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p1 = new JPanel(new FlowLayout());
p1.add(lab);
cp.add(p1, BorderLayout.NORTH);
cp.add(drawing, BorderLayout.CENTER);
JPanel p2 = new JPanel(new FlowLayout());
p2.add(new CountButton("Count", count, this));
p2.add(new ExitButton("Quit"));
cp.add(p2, BorderLayout.SOUTH);
setTitle("Example4");
setSize(200,150);
setVisible(true);
}
Figure 10.20: view class with border layout and panels (concl.)
import java.awt.*;
import javax.swing.*;
/** Drawing creates a panel that displays a small drawing */
public class Drawing extends JPanel
{ private Counter count; // the model object
The first statement simultaneously creates a panel and sets its layout. Components
are added to panels just like they are to frames, and panels are added to frames just
like other components are added.
Class Drawing extends JPanel. It repaints the drawing when told.
An image file can be displayed within a panel by using the panel’s graphics pen.
Insert these statements into the panel’s paintComponent method:
ImageIcon i = new ImageIcon("mypicture.gif");
Image j = i.getImage();
594
The first statement converts the image file into an ImageIcon, as we did for creating
labels for buttons. The second statement extracts an Image object from the icon,
and the third employs the panel’s graphics pen to draw the image with its upper left
corner at position 20, 20.
Exercises
1. Test Example4 by pushing its Count button 10 times. What happens to the
drawing? Resize the frame to a larger size; what do you observe? Propose a
solution to this problem.
2. Experiment with border layout: Rewrite Frame4 in Figure 20 so that the three
panels are inserted into the east, center, and west regions, respectively; into the
center, north, and east regions, respectively. Next, delete panel p1 from the
constructor method and insert the label directly into the north region.
3. Write a application that lets you grow and shrink an egg: The GUI has two
buttons, Grow and Shrink. An egg is displayed underneath the buttons; when
you push Grow, the egg gets 10% larger; when you push Shrink, the egg becomes
10% smaller. Use method paintAnEgg from Figure 2, Chapter 5, to paint the
egg.
Animations, like the moving ball example in Chapter 7, can be easily reformatted
to appear within a panel of a GUI. The GUI might also display buttons that, when
pressed, alter the animation’s progress—video games are built this way.
Here is a simple example. Perhaps we have an animation that displays a ball that
10.6. RICHER LAYOUT: PANELS AND BORDERS 595
ThrobController ThrobbingBall
When the button is pressed, the ball in the animation changes color; in this simple
way, the user “plays” the animation.
The animation is displayed in a panel that is embedded into a frame. Like the ani-
mation in Chapter 7, this animation has its own controller, and there is an additional
button-controller for changing the color of the ball. Figure 21 displays the architec-
ture. The ball is modelled by class ThrobbingBall; the view consists of ThrobPanel,
which paints the ball on a panel, and ThrobFrame, which displays the panel and the
color-change button on a frame. Figure 22 displays these classes.
The application’s controllers hold the most interest: ThrobController contains
a run method whose loop resizes the throbbing ball and redraws it on the panel;
ColorButton changes the ball’s color each time its button is pressed. The controllers
are connected to the model and view by means of the start-up class, StartThrob. All
three classes appear in Figure 23.
Because the ThrobController’s run method is a nonterminating loop, it is crucial
that this method is invoked as the last statement in the animation. (The section,
596
/** throb makes the ball change state between large and small */
public void throb() { is it currently large = !is it currently large; }
}
import java.awt.*;
import javax.swing.*;
/** ThrobPanel draws a throbbing ball */
public class ThrobPanel extends JPanel
{ private int panel size; // size of this panel
private int location; // where ball will be painted on the panel
private int ball size; // the size of a ‘‘large’’ ball
private Color c = Color.red; // the ball’s color
private ThrobbingBall ball; // the ball object
...
10.6. RICHER LAYOUT: PANELS AND BORDERS 597
import java.awt.*;
import javax.swing.*;
/** ThrobFrame displays the throbbing-ball panel and color-change button */
public class ThrobFrame extends JFrame
{ /** Constructor ThrobFrame builds the frame
* @param size - the frame’s width
* @param p - the panel that displays the ball
* @param b - the color-change button */
public ThrobFrame(int size, ThrobPanel p, ColorButton b)
{ Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(p, BorderLayout.CENTER);
cp.add(b, BorderLayout.SOUTH);
setTitle("Throb");
setSize(size, size + 40);
setVisible(true);
}
}
598
public ColorButton(ThrobPanel f)
{ super("OK");
view = f;
addActionListener(this);
}
“Threads of Execution,” at the end of this chapter explains why.) It is also crucial
that the loop within run has a delay, because it is during the period of delay that
the computer detects action events and executes action listeners.
Exercises
2. Swap the last two statements of main in StartThrob. Explain what the anima-
tion no longer operates.
The frame is laid out as a 4-by-4 grid, where each cell of the grid holds a button-
controller object created from class PuzzleButton (which will be studied momentar-
ily). Figure 24 shows class PuzzleFrame, the puzzle’s GUI.
A grid layout of m rows by n columns is created by new GridLayout(m, n). Adding
components to a grid layout is done with the add method, just like with flow layout.
In the constructor method in the Figure, the nested for-loop creates multiple distinct
PuzzleButton objects and inserts them into a grid layout; the grid is filled row by
row. As explained in the next paragraph, it is helpful to retain the addresses of the
button objects in an array, named button, but such an array is not itself required
when using grid layout.
Each button displays a numerical label on its face, stating the numbered piece it
represents. A user “moves” a slide piece by clicking on it. Unfortunately, the buttons
themselves do not move—the labels on the buttons move, instead. When a button
is clicked, its event handler sends a message to method update, which consults array
button and uses the setBackground and setText methods to repaint the faces of the
buttons.
Figure 25 displays the controller class, PuzzleButton. When pushed, the button’s
actionPerformed method uses getText() to ask itself the number attached to its face;
it then tries to move that number in the model. If the move is successful, the view is
told to update.
The controller in Figure 25 should be contrasted with the one in Figure 12, Chapter
8. The Chapter 8 controller used one central loop to read integers and make moves
602
forever. But the just class just seen is used to construct multiple controllers, each of
which is programmed to make just one move. There is no loop to control the moves.
Instead, the application is controlled by the user, who can push buttons forever.
Whenever the user tires, the application patiently waits for more events.
Exercises
1. Experiment with grid layout: Rewrite Frame4 in Figure 20 so that the three com-
ponents are added into a frame with new GridLayout(3,1); with new GridLayout(1,3);
with new GridLayout(2,2).
2. Add to the GUI in Figure 24 a label that displays the count of the number of
pieces that have been moved since the slide-puzzle was started. (Hint: Use the
counter from Figure 9.)
3. Create a GUI that looks like a calculator. The GUI displays 9 buttons, arranged
in a 3-by-3 grid, and a numerical display (label) attached to the top.
When the user pushes the Go button, an action event is triggered, and the associated
action listener increments the counter associated with the selected item. For example,
if the Go button was clicked on the above interface, we obtain this new view:
In this manner, a scrolling list can present choices, like buttons do, and give useful
output, like labels do.
A scrolling list is built in several steps: You must create a “model” of the list,
you must insert the model into a JList object, and you must insert the JList into a
JScrollPane object to get a scroll bar. These steps might look like this:
Whatever strings that are assigned to the elements of list labels will be displayed
as the items of the list, e.g.,
for ( int i = 0; i != list_labels.length; i = i+1 )
{ list_labels[i] = "Counter " + i + " has 0"; }
/** Counter2 is a Counter that states its identity with a toString method */
public class Counter2 extends Counter
{ private int my index;
a toString method, which JList’s internal view uses to display the objects as list
items.
Here is how we build the example program displayed above:
• We create a view that contains a JList whose internal model is exactly the
array of counters.
• We add a Go button that, when pushed, asks the JList which Counter2 item
was selected and then tells that item to increment itself.
Figure 26 shows the model and view classes that generates the example, and Figure
27 shows the controller-button that updates the model.
10.8. SCROLLING LISTS 607
Figure 10.26: model and view for scrolling list of counters (concl.)
import java.awt.*;
import javax.swing.*;
/** ListFrame shows a scrolling list */
public class ListFrame extends JFrame
{ private Counter2[] counters; // the address of the model object
private JList items; // the list that displays the model’s elements
Class ListFrame builds its scrolling list, items, from the array of Counter2[] ob-
jects that its constructor method receives. When the frame is made visible, the view
part of items automatically sends toString messages to each of its array elements
and it displays the strings that are returned in the list on the display.
ListFrame is equipped with two small but important public methods, getSelection
and update. When invoked, the first asks the list for which item, if any, is selected at
this time by the user. The second method tells the list to deselect the item so that
no list item is selected.
Perhaps the user pushes the Go button; the actionPerformed method for ListButton
sends a getSelection message to the view and uses the reply to increment the ap-
propriate counter in the model. Then an update message deselects the selected item.
Once actionPerformed finishes, the scrolling list is redrawn on the display, meaning
that the toString methods of the counters report their new values.
It is possible to attach event listeners directly to the scrolling list, so that each
time the user selects an item, an event is generated and an event handler is in-
voked. A controller that handles such list selection events must implement the
10.8. SCROLLING LISTS 609
import javax.swing.*;
import javax.swing.event.*;
/** ListController builds controllers for lists of counters */
public class ListController implements ListSelectionListener
{ private Counter2[] counters; // address of model object
private ListFrame view; // address of view object
Then, within the constructor method of class ListFrame, we attach the controller as
a listener to the JList items:
This makes the list’s items behave as if they are buttons, all connected to the same
controller.
Finally, it is possible to tell a scrolling list to allow simultaneous selection of
multiple items;
items.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
tells list items to allow multiple selections. Of course, when an event listener examines
the list, it should ask for all the selected items; this is done by
items.getSelectedIndices()
Exercises
1. Create a GUI that displays a scrolling list whose items are the first 10 letters
of the alphabet. (Hint: use as the list’s model this array: String [] letters =
{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}.)
2. Augment the GUI from the previous Exercise with a label and a button. When
the user selects a list item and pushes the button, the letter on the selected list
item is displayed on the label.
3. Augment the GUI from the previous Exercise so that when the user pushes
the button, the text in the selected item is “doubled,” e.g., if a is selected, it
becomes aa.
4. Create a list that contains the strings, Red, Yellow, and Blue. Insert the list
and a button into a GUI, and program the button so that when the user pushes
it, the GUI’s background is colored with the color selected in the list.
The first statement creates a JTextField object that displays 8 characters of text and
initially shows the string, "0".
The standard operations one does with a text field is extract the text the user has
typed, e.g.,
String data = input_text.getText();
and reset the text in the text field with a new string:
input_text.setText("0");
10.9. TEXT FIELDS 611
These operations might be used when, say, the user pushes a button that starts an
action listener that extracts and resets the text field’s contents.
As Figure 5 indicates, a JTextField is a subclass of a JTextComponent and therefore
inherits a variety of methods for cutting, copying, pasting, and selecting. We will not
study these methods for the moment; they are more useful for so-called text areas
(multi-line text components), which we study in a later section.
To show use of a text field, we develop a simple temperature convertor, which
accepts a numerical temperature, either Fahrenheit or Celsius, and converts it to the
equivalent temperature of the other scale. When the user types a temperature into
a text field, selects either the Celsius or Fahrenheit scale, and pushes the Go button,
the result is displayed in the view:
Figure 28 displays the view class for the temperature converter. It is written in
two parts: An abstract class provides all the methods but one, and a concrete class
extends the abstract one with a coding for displayError, a method that displays
error messages.
The view’s public methods will be used by the controllers, ResetButton and
ComputeTempButton, to fetch the text the user typed, to display the answer that the
model computes, and to reset the text field for another conversion. Figure 29 displays
the two controllers as well as a simplistic model class, TempCalculator.
ComputeTempButton uses the model to convert temperatures. For the moment, ig-
nore the try...catch exception handler within the button’s actionPerformed method
and examine its interior: The controller sends the view a getInputs message to receive
an array of two strings, one containing the input temperature and one containing the
temperature’s scale. The first string is converted into a double. Assuming that the
conversion is successful, the scale is examined and used to choose the correct conver-
sion method from the model object. Finally, the view’s displayAnswer method shows
the converted temperature.
What happens if the user types a bad temperature, e.g., "abc0"? In this case, the
statement,
612
ResetButton AbsTempFrame
actionPerformed getInputs JFrame
ActionListener displayAnswer
actionPerformed displayError
ComputeTempButton resetFields
actionPerformed
TemperatureCalculator TempFrame
celsiusIntoFahrenheit displayError
fahrenheitIntoCelsius
try { ...
double start_temp = new Double(s[0].trim()).doubleValue();
...
}
catch(RuntimeException e) // if s[0] is nonnumeric, an exception occurs
{ view.displayError(e.getMessage()); }
Finally, when a user types text into a text field, she often terminates her typing
by pressing the Enter key. If we desire, we can make the Enter key generate an action
event; all we need do is add an action listener to the text field object. In Figure 28,
the text field was constructed as follows:
input_text.addActionListener(compute_controller);
This registers the compute controller as the event handler for action events for
input text. Now, either a press of the Enter key or a click on the Go button generates
an action event that is handled by actionPerformed in class ComputeTempButton.
Exercises
1. Create a GUI that contains a text field, a button, and a label. When the user
pushes the button, whatever text that appears in the text field is copied to the
label, and the text field is reset to hold an empty string.
2. Create a child’s arithmetic calculator: Its interface displays two text fields, a
label, and a four-item list, where the items are +, -, *, and /. When the child
types two integers into the two text fields and selects one of the four items, the
calculator computes the selected arithmetic operation on the two numbers and
displays the result in the label.
3. Revise the temperature converter GUI in Figure 28 so that its answer label is
removed and is replaced by the output view class, TemperaturesWriter in Figure
6, Chapter 5. (Hint: Change its header line to read, class TemperaturesWriter
extends JPanel, and remove the setTitle method from its constructor method.
Now, you have a panel you can insert into the AbsTempFrame in Figure 28.)
4. Make a GUI that displays a ten-item list, where all ten items are blank. The
GUI also has a text field. When the user types a word into the text field and
presses Enter, the word is copied into the lowest-numbered blank item in the
list, if it is not in the list already. (If the word is already in the list or if the list
is completely filled, no action is taken.)
Figure 3 at the beginning of the chapter showed the dialog that the tempera-
ture converter program might produce when a user enters an invalid temperature for
conversion.
As we already know, the AWT/Swing framework contains a class JOptionPane
that lets one simply generate forms of dialogs. The simplest form of dialog, a message
dialog,
is created by stating,
where owner is the address of the frame to which the dialog refers. The owner is
supplied to the dialog so that the dialog can be displayed near its owner. (If owner
is null, the dialog appears in the center of the display.)
Execution of the application pauses while the dialog appears on the display; once
the user pushes the OK button, execution resumes at the position in the program
where the dialog was created.
In the previous section, we studied a temperature converter application that
displayed error messages in a label in the application’s view. The view was con-
structed from a class, TempFrame (see Figure 28), which extended an abstract class,
AbsTempFrame (Figure 28). We can easily extend AbsTempFrame to build a frame that
displays error messages within message dialogs. Figure 31 presents class TempFrame2,
whose displayError method generates message dialogs, like the one in Figure 3.
Recall that displayError is invoked from the actionPerformed method within
class ComputeTempButton (Figure 29) when a bad number has been typed into the
frame’s text field. The dialog halts execution of the frame until the user pushes OK
(or the “X”-button at the dialog’s top right corner). We initiate this variant of the
temperature converter with this class:
A second form of dialog is the confirm dialog, which displays a message and asks
10.10. ERROR REPORTING WITH DIALOGS 619
Again, owner is the address of the component whose execution should be paused while
the dialog appears. When the user pushes one of the Yes, or No, or Cancel buttons,
an integer value is returned, and in the above case, saved in variable i. The value
can be queried, e.g.,
if ( i == JOptionPane.YES_OPTION )
{ System.out.println("‘Yes’ was pushed"); }
When the user types a string into the text field and pushes OK, the string is returned,
and in this case, assigned to s. If the user types nothing but pushes OK, the empty
string, "", is returned. If the user pushes Cancel, a null value is returned, regardless
of what was typed into the text field. Finally, if the user pushes the “X”-button, null
is returned as well.
Exercises
1. Write a GUI with three buttons and a label. When the user pushes the first
button, a message dialog appears; when the user pushes the button on the
confirm dialog, the label displays, Message dialog dismissed. When the user
pushes the second button, a confirm dialog appears; when the user pushes a
button on the confirm dialog, the label displays the name of the button pushed.
When the user pushes the third button, an input dialog appears; when the user
enters text and pushes the dialog’s Ok button, the label displays the text the
user typed.
large white area. The controller for a text area responds to a user’s typing by copying
the typed letters into the model part and refreshing the view part so that the user
sees the text she typed. The controller also allows the user to move the insertion
caret by clicking the mouse and to select text by dragging the mouse.
An easy way to create a text area and embed it into a frame goes as follows:
Container cp = getContentPane();
...
JTextArea text = new JTextArea("", 20, 40);
text.setLineWrap(true);
text.setFont(new Font("Courier", Font.PLAIN, 14));
JScrollPane sp = new JScrollPane(text);
cp.add(sp);
In the above example, the constructor, JTextArea("", 20, 40), creates a text area
that displays 20 lines, each line of length 40 columns, where the empty string is dis-
played initially. (That is, the text area is a big, empty space.) Next, text.setLineWrap(true)
tells the text area to “wrap” a line of length greater than 40 by spilling the extra
characters onto the next line.
If we desire a font different from the text area’s default, we state text.setFont(new
Font("Courier", Font.PLAIN, 14)), where setFont sets the font and new Font(name,
style, size) creates a font in the name, style, and point size that we desire. Standard
examples of font names are "Courier", "TimesRoman", and "SansSerif"; styles include
Font.PLAIN, Font.BOLD, and Font.ITALIC; and sizes between 10 and 16 points are
commonly available.
The fourth statement embeds the text area into a scroll bar, so that user input
exceeding 20 lines can be scrolled forwards and backwards. Finally, the scrolling text
area is embedded into the content pane. The text area in Figure 4 was assembled
essentially in this manner.
Text areas possess a wide variety of methods, many of which are inherited from
its superclass, JTextComponent. Table 32 lists some of the most useful ones. Rather
than study the methods in the Table now, we encounter them in the case study in
the next section.
A text area’s default controller processes typed text, including Backspace and
Enter keys, as expected. It processes mouse movements, clicks, and drags correctly
as well. Rather than attach event listeners directly to a text area, we should create
menus that we hang above the text area. When a user selects an item from a menu,
this generates an action event that can signal an event handler that can examine and
update the text area.
Menu items are embedded into menus, which are embedded into a menu bar,
which is embedded into a frame. For example, the two menus displayed in Figure
4 at Chapter’s beginning can be created by these statements inside the constructor
method of a frame:
622
abstract class
JTextComponent
Methods
getText(): String Return the entire text contents of the component
as one string.
setText(String s) Reset the text contents of the components to s.
getCaretPosition(): int Return the character position where the insertion
caret is positioned.
setCaretPosition(int p) Move the insertion caret to position p.
moveCaretPosition(int p) Like setCaretPosition but also selects the text
that falls between the previous caret position and
the new position, p.
getSelectedText(): String Return the string that was selected by the user by
dragging the mouse across the string.
getSelectionStart(): int Return the index of the first character of the se-
lected text.
getSelectionEnd(): int Return the index of the last character, plus one, of
the selected text.
cut() Remove the selected text and hold it in the compo-
nent’s clipboard.
copy() Copy the selected text into the component’s clip-
board.
paste() Insert a copy of the text in the component’s clip-
board at the caret position.
isEditable(): boolean Return whether the user may alter the contents of
the text component.
setEditable(boolean b) Set whether the user may alter the contents of the
text component.
Menu items act like buttons—when selected, they generate action events. As written,
the above statements do not attach action listeners to the menu items, but we can
do so in the same way that we have done for buttons. We pursue this in the next
section.
ClearMenuItem
QuitMenuItem EditModel JTextArea
CutMenuItem
CopyMenuItem
PasteMenuItem EditFrame
FindMenuItem
JFrame
ReplaceMenuItem ReplaceFrame
seen momentarily. (For the moment, pretend the menu items are like the buttons we
have used.)
Next, Figure 36 shows the model/text area, EditModel. The model extends a
JTextArea with methods to clear the text area and to find strings within it. The
most interesting method, find, fetches the text within the text area with the getText
method. (See Table 32.) Next, it uses a string search method for strings: text.indexOf(s,
position) returns the index within text, starting from position, where string s first
appears. (If s does not appear, -1 is returned.) Next, the caret is moved to the end
position where s was found (by using setCaretPosition) and is dragged backwards,
selecting the found string, by moveCaretPosition. (See Table 32.)
The model’s methods are used by the various menu items, which are listed in
Figure 37.
The majority of the menu items do no more than send a single message to
EditModel; these menu items are written as subclasses of an abstract class, EditorMenuItem.
FindMenuItem works a bit harder: Its actionPerformed method generates an input
dialog that asks the user for a string to find. Then, the EditModel’s findFromCaret
method is asked to locate the string. If the string is not found, the user is asked, by
means of a confirm dialog, if the search should be performed from the front of the
text area. If the user so wishes, this is done.
The last menu item, ReplaceMenuItem, displays the ReplaceFrame, which helps a
user find a string and replace it by another. The ReplaceFrame is depicted in Figure
38.
Although it increases the size of the class and limits flexibility, we have made
ReplaceFrame the action listener for its three buttons. This shows how one actionPerformed
method can handle three different button pushes in three different ways—the mes-
sage, e.getSource(), asks parameter e the identity of the button that was pushed, and
based on the answer, the appropriate steps are executed. The method, replaceRange,
in the text area is used to replace the string that is found by the string the user typed
as the replacement; see Table 28.
Exercises
1. Add to the text editor’s GUI a menu that lists these font sizes: 12, 14, and 16.
When the user selects one of the sizes from the menu, the text displayed in the
text area changes to the selected size.
2. You can add buttons to a menu–try this: Modify class AbsTempFrame in Figure
28 so that it uses a menu to hold the Go, Reset, and Bye buttons.
3. Create an “appointments” GUI that displays a text area, a Save button, and a
five-item menu consisting of Monday, Tuesday, Wednesday, Thursday, and Friday.
When the GUI’s user selects one of the menu items, the text area displays a
message saved for the item. (Initially, all the messages for all items are empty.)
10.11. TEXTAREAS AND MENUS 627
/** find locates string s in the text area, starting from position */
private int find(String s, int position)
{ String text = getText();
int index = text.indexOf(s, position); // see Table 9, Chapter 3
if ( index != -1 ) // did we find string s?
{ setCaretPosition(index + s.length()); // resets the caret
moveCaretPosition(index); // selects the string
}
return index;
}
/** findFromStart locates a string starting from the front of the text area
* @param s - the string to be found
* @return the position where s is first found; -1, if s not found */
public int findFromStart(String s)
{ return find(s, 0); }
/** myModel returns the address of the model this menu item manipulates */
public EditModel myModel()
{ return buffer; }
import java.awt.event.*;
/** ClearMenuItem clears a text area */
public class ClearMenuItem extends EditorMenuItem
{ public ClearMenuItem(String label, EditModel model)
{ super(label, model); }
import java.awt.event.*;
/** CopyMenuItem copies selected text into the clipboard */
public class CopyMenuItem extends EditorMenuItem
{ public CopyMenuItem(String label, EditModel model)
{ super(label, model); }
import java.awt.event.*;
/** PasteMenuItem moves contents of the clipboard into the text area */
public class PasteMenuItem extends EditorMenuItem
{ public PasteMenuItem(String label, EditModel model)
{ super(label, model); }
The user can edit the message, and when she pushes Save, the message is saved
with the selected item.
4. Create a GUI that displays two text areas. Write controllers that let a user cut,
copy, and paste text from one text area to the other.
In the general case, a component can have multiple listeners and a listener can be
registered with multiple components. (But for simplicity, we usually match com-
ponents with their listeners on a one-one basis.) When an event occurs within the
component, the addresses of all its listeners are fetched, and each listener is sent an
actionPerformed message.
It is possible to implement listeners and event handling for nongraphical compo-
nents. An object can be made to generate its own “events,” and when an object does
this, the object’s Observers are sent an update message, which is asking the Observer
to “handle” the event. Object, ob, is added to object b’s Observers by the message,
b.addObserver(ob).
Here is an example. We rewrite class Counter in Figure 9 so that it can generate
its own events for its Observers. The new class appears in Figure 39. The class begins
with import java.util.*, and it extends Observable; this gives the class two new
methods, setChanged and notifyObservers, which are used in the class’s increment to
generate an event and to signal the class’s Observers. The class also inherits another
method, addObserver, which we see momentarily.
An important aspect of the Figure is that the counter does not know who its
Observers are. This ensures that the counter is not “coupled” too strongly to the
634
other classes in the program. (See Chapter 6 for a discussion about coupling.)
Next, say that another class, call it class PrintCount, wishes to print a message
to the display every time the counter is incremented. The class must register itself as
one of the Observers of Counter3. It can be done like this:
import java.util.*;
public class PrintCount implements Observer
{ private Counter3 count;
public PrintCount(Counter3 c)
{ count = c;
count.addObserver(this); // this object registers as an Observer of count
}
The class implements Observer (see Figure 7), which means that it possesses an
update method. With the message, count.addObserver(this), the newly created
PrintCount object registers itself as an Observer of its Counter3 parameter. Now,
whenever the counter object generates a notifyObservers message, PrintCount’s
update method will be invoked.
The Observer interface requires that update possess two parameters; the first
contains the address of the object that sent the notifyObservers message. (In the
above example, this must be the counter object.) The second parameter holds a
value, v, that is sent when the observed object sends a notifyObservers(v) message.
(In the above example, the second argument will have value null, since the simpler
message, notifyObservers(), was used.)
As noted above, an advantage to using class Observable and interface Observer
is that it lets one class activate the method of a second class without knowing the
identity of the second class. We exploit this in the next section. But a major disad-
vantage is that the class that generates the events extends Observable. Since Java
allows a class to extend at most one other class, this makes it impossible to use class
Observable with any class that already extends another. For example, we cannot
revise the scrolling list example in Figure 26 so that class Counter2 extends class
Observable, because Counter2 already extends class Counter from Figure 9. (The
best we can do is revise the header line of class Counter2 in Figure 26 so that it
extends Counter3, producing a slightly convoluted development.)
10.12. EVENT-DRIVEN PROGRAMMING WITH OBSERVERS 635
Frame4a
Observable
Exercises
1. For practice, return to Figures 13-15. Replace class Counter by class Counter3
in Figure 39; revise class Frame2c to be an Observer of Counter3; and simplify
class CountButton so that it no longer sends a message to the view. Redraw the
application’s class diagram and compare it to Figure 15; to how many classes
is the controller coupled? the model? the view?
10.13 Summary
We studied how to design and built graphical user interfaces (GUIs) for applications.
Here are the new concepts:
10.13. SUMMARY 637
/** Constructor Frame4a creates a frame with label, drawing, and 2 buttons
* @param c - the model object, a counter
* @param drawing - a panel that displays a drawing */
public Frame4a(Counter3 c, JPanel drawing)
{ count = c;
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p1 = new JPanel(new FlowLayout());
p1.add(lab);
cp.add(p1, BorderLayout.NORTH);
cp.add(drawing, BorderLayout.CENTER);
JPanel p2 = new JPanel(new FlowLayout());
p2.add(new Count3Button("Count", count));
p2.add(new ExitButton("Quit"));
cp.add(p2, BorderLayout.SOUTH);
setTitle("Example4");
setSize(200,150);
setVisible(true);
}
New Terminology
• graphical user interface (GUI): an input/output view that lets a user submit
input by pressing buttons, selecting menu items, typing text, etc., and shows
the user the output in graphical form.
• event handler (event listener): a controller component that receives and reacts
to an event; this is called handling the event.
• java.awt and javax.swing: the Java packages that contain classes that help a
programmer write GUIs. Called AWT/Swing, for short.
• component: the Java term for a graphical object that has a position, a size, and
can have events occur within it.
• dialog: a temporary window that can appear and disappear while an application
executes. There are three forms:
• label: a component that displays text that the user can read but cannot alter
• text component: a component into which a user can type text; this can be a text
field, into which one line of text is typed, or a text area, into which multiple
lines can be typed.
640
• layout: the manner in which a collection of components are arranged for display
in a GUI. Some forms of layout are
– flow layout: the components are arranged in a linear order, like the words
in a line of text
– border layout: components are explicitly assigned to the “north,” “south,”
“east,” “west,” or “center” regions of the container
– grid layout: components are arranged as equally-sized items in rows and
columns, like the entries of a matrix or grid
• content pane: the part of a frame where components are inserted for display
• glass pane: the part of a frame that “overlays” the content pane. Normally left
“clear” but it can be painted upon by means of a frame’s paint method.
• list selection event: a form of event caused by selecting an item from a list. Such
events are handled by list selection listeners.
Points to Remember
Some principles to remember are
• Graphical components can generate events, e.g., button pushes or text inser-
tions. Therefore, an application becomes event driven—the events generated by
button pushes and text insertions, activate controllers (event handlers or event
listeners) that send messages to the application’s model to compute results.
model for the overall application—one example is a text editor program, whose
model is a text area component.
• The standard MVC architecture for an application with a GUI uses the observer
pattern to connect its components: events from the GUI trigger controllers. A
controller sends a message to the model to compute. The model computes
answers and signals its Observers (the view) that new results await display.
The Observers fetch the results from the model and display them on the GUI.
This pattern decouples controllers from views and encourages better reuse of
controller components.
2. For any Programming Project that you worked in Chapter 6, write a GUI for
it. For example, if you built Project 2, Chapter 6 (the mortgage calculator),
then write a helpful GUI into which a user can type principal, interest, and year
values and receive answers regarding monthly payments and totals.
3. For any Programming Project that you worked in Chapter 7, write a GUI for
it. For example, if you built Project 3, Chapter 7 (the lottery-odds calculator),
then write a helpful GUI into which a user can type her lottery picks, get the
odds of winning, and then generate a sample play of the lottery.
5. Write an application with a GUI that lets two humans play checkers by clicking
on the pieces, which are positioned on an 8-by-8 grid.
6. Build an application that looks and behaves like a typical hand-held calculator,
with a grid of buttons for numerals and arithmetic operations and a display at
the top that displays the answers of the calculations.
7. Build an appointments-manager application. The application lets a user store,
edit, and view appointments that are saved according to a particular day of
the month and a particular hour of the day. The application might behave as
follows. Initially, the days of the current month are displayed, on buttons:
642
-----------------------------------
| | 1 | 2 | 3 | 4 | 5 | 6 |
----------------------------------
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
---------------------------------
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
----------------------------------
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
---------------------------------
| 28 | 29 | 30 | 31 | | | |
---------------------------------
When a user moves the mouse over a date and clicks, a new window appears that
displays a summary of the appointments for the selected date. For example, if
the 8th is selected, we might see the following window appear:
-----------------------------
| Appointments for the 8th:
| ----------------------
| | 8:30 French 111 class.
| | 10:30 Calculus class.
| | 12:00 Lunch with Fred.
| -----------------------
-----------------------------
In addition, the above window must have buttons or menus or text fields that
let a user view and edit the details of an appointment, add an appointment,
delete an appointment, etc. For example, one might wish to view the details
of the 8:30 appointment on the 8th. By somehow selecting and clicking, a new
window (or a text area within the existing window) might display:
--------------------------------------
| 8:30 French 111 class.
| Exam followed by video for Lesson 12;
| remember to study!
---------------------------------------
The above window might have additional buttons or menus that let one modify,
save, or even delete the appointment.
8. Build an application that indexes a user’s book or music collection. The ap-
plication helps a user organize her book listings by genre (e.g., novel, mystery,
biography), by author, and by title. A user can enter new books and can query
the listing of books by genre, by author, or by title.
10.14. PROGRAMMING PROJECTS 643
When you design the application’s GUI, consider the merits and drawbacks of
having the GUI be one large window, whose buttons and menus perform all the
actions listed above, versus a collection of smaller, specialized windows, which
can appear and disappear as needed to perform an operation.
9. Design and build a “pebble dropping game,” where a human alternates with
a computer at dropping pebbles into six vertical chutes. Like tic-tac-toe, the
objective is to be the first player to arrange pebbles in a row, vertically, hori-
zontally, or diagonally. The game board first appears with six empty chutes:
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
-----------
The human selects a chute and drops a pebble in it; the pebble falls as far as it
can:
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | |X| | | |
-----------
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | |X|O| | |
-----------
| | | | | | |
| | | | | | |
| | | | | | |
644
| | | | | | |
| | | |X| | |
| | |X|O| | |
-----------
The game continues in this way until a player wins or all chutes are full.
10. Build an animated version of the pebble dropping game, called “Tetris”: Given
a playing area 6 columns wide and 12 rows high, the computer generates 4-by-4
blocks that appear at random positions above the playing area and fall to the
bottom. As they fall, the human player can operate controls that move a falling
block to the left or right. The objective is to evenly stack the falling blocks
in the playing area so that the maximum number of blocks can be saved. The
game ends when the pile of blocks reaches the top of the playing area.
(a) Build the basic Tetris game described above, and add a score counter that
remembers the number of blocks stacked until the game ends.
(b) Extend the game so that when a row is completely occupied with blocks,
that row disappears from the playing area, and the rows of blocks above
it drop down one row.
(c) Extend the game so that in addition to 4-by-4 blocks,
- -
| | |
- -
| | |
- -
the computer drops these 1-by-4 and 2-by-3 configurations:
- - - - - -
| | | | | | | |
- - - - - - -
| | |
- -
When the computer initially drops the two new shapes, it can randomly
rotate them, e.g., the 1-by-4 shape might be rotated 90 degrees so that
it looks like a 4-by-1 shape. Add controls that rotate the falling shapes
clockwise and counterclockwise.
(d) Modify the game so that the blocks fall downwards faster as a player’s
score increases.
11. Build an animated berry-eating game, called “Pac-Man”: The game board is
a 12-by-12 grid. Initially, one game square is occupied by the Pac-Man; the
10.15. BEYOND THE BASICS 645
When the game starts, the Pac-Man moves forwards from square to square,
eating each berry it finds on a square that it occupies. The human must tell
the Pac-Man when to change direction, e.g., turn left or turn right.
(a) Build the basic Pac-Man game described above; attach a clock that counts
the elapsed time until the Pac-Man eats all the berries on the board.
(b) Modify the game board so that it is a maze; that is, some squares are
occupied by barriers or separated by barriers.
(c) Extend the game so that, approximately every 5 seconds, 3 “goblins” ap-
pear on the game board at random positions. Moving at the same speed as
the Pac-Man, each goblin tries to “eat” the Pac-Man. If a goblin succeeds,
the game ends. The goblins disappear after 5 seconds.
12. Build an animated chase game, called “Frogger”: The playing area consists of 6
rows, which are traffic lanes upon which animated cars and trucks travel. The
game’s player is represented by a “frog,” whose objective is to move across all 6
rows without being hit by a moving vehicle. (That is, a vehicle “hits” the frog
if it occupies the same playing space as the frog.)
(a) Build an initial version of the game where there are no vehicles. When the
game starts, the frog appears at a random space in the bottommost row of
the board. With the use of controls, a player can move the frog forwards
or turn the frog left or right.
(b) Next, create one vehicle for each traffic lane. Each vehicle traverses its
lane, and after a short random time delay, reappears to traverse its lane
again. Stop the game if a vehicle and the frog land in the same playing
space.
(c) Modify the game so that a seventh row is placed in the middle of the
playing area. No vehicles travel on this “divider,” but the frog can move
onto this space and “rest.”
(d) Modify the game so that vehicles randomly travel at different velocities.
10.15.1 Applets
At the end of Chapter 4, we saw how to convert an application into an applet. The
same technique can be applied, without change, to convert a GUI-based application
into an applet: The applet is constructed from the application’s view class, so that
instead of extending JFrame the view class extends JApplet. We follow the following
steps:
• Remove the setSize, setTitle, and setVisible statements from the frame’s
constructor method. Also, remove the addWindowListener statement, if any.
• Move the instructions in the application’s main method into the frame’s con-
structor method, and rename the constructor method public void init().
• Write an HTML file for a Web page that starts the applet. A sample file might
look like this:
Once these changes are made, test the applet with the appletviewer program (which
is available as part of your IDE or as part of the JDK; see Chapter 4), and once
the applet meets with your satisfaction, only then use a web browser to see the final
10.15. BEYOND THE BASICS 647
result. What you will see is the program’s interface embedded into the Web page at
the position where you placed the applet command.
For example, the GUI in Figure 24 for the slide puzzle is converted into an applet,
PuzzleApplet, by making the above-listed changes; Figure 44 shows the result.
The other model and view classes stay the same—the program can create dialogs
and frames, use lists, menus, and buttons, just like before. One pragmatic disad-
vantage is that older models of Web browsers might not work correctly with all the
AWT/Swing components. This problem should disappear quickly as time passes and
proper Web browsers appear.
As seen above, the applet command in the HTML file sets the size of an applet
and automatically displays it. It is also possible for the HTML file to transmit actual
parameters into an applet. For example, perhaps the web page that displays the slide
puzzle sets the puzzle’s size. This can be done with the following two modifications:
1. Modify the applet command in the HTML page to use a parameter command:
2. Within the applet’s init method, replace the statement, size = 4, by the fol-
lowing:
String s = getParameter("size");
size = new Integer(s).intValue();
import javax.swing.*;
public class ExampleApplet extends JApplet
{
public void init()
{ ... the body of the main method goes here ... }
}
648
/** update consults the model and redraws the puzzle grid. */
public void update()
{ ... } // as before; see Figure 24
}
10.15. BEYOND THE BASICS 649
For example, if we return to the slide-puzzle example in Figure 24, we can leave
class PuzzleFrame and all its collaborators untouched; we merely revise the main
method in the startup class, Puzzle, into the following:
import javax.swing.*;
public class PuzzleGeneratorApplet extends JApplet
{ public void init()
{ int size = 4; // a 4 x 4 slide puzzle
SlidePuzzleBoard board = new SlidePuzzleBoard(size);
PuzzleFrame frame = new PuzzleFrame(size, board);
}
}
we will see the greeting in the web browser and a separate, newly created slide puzzle.
The slide puzzle will execute as long as the web browser itself executes.
Like other graphical components, a JTable has its own internal view, controller,
and model. All three can be customized, but we spend our efforts on the model, which
is the the part that a programmer must build for her JTable. We call the model a
table model. A table model must be built from a class that implements interface
TableModel, which is displayed in Figure 46. The interface lists behaviors that a table
model must have so that it can be read, updated, and displayed by the controller part
and the view part of a JTable. We study the interface’s methods in the examples
that follow.
The best way of building a class that implements TableModel is to extend an
AWT/Swing prebuilt abstract class, AbstractTableModel. This class has codings for
all the methods listed in Figure 46, except for getRowCount, getColumnCount, and
getValueAt. So, a programmer can quickly construct a table model by extending
AbstractTableModel with the missing methods.
Figure 47 shows the table model built this way for the view in Figure 45.
The class, VoteModel, extends AbstractTableModel. It uses three arrays to re-
member the candidate names that should appear in Column 0, the region names that
should appear above the columns, and the vote counts for each candidate in each
region. The class implements the missing three methods and revises getColumnName
so that the table has useful labels for the columns. (Without the last method, the
table model would be displayed with labels like A, B, and C.)
Since the table model will be used to count votes in an election, two additional
methods, getVoteCount and changeVoteCount, are inserted. The latter invokes an
AbstractTableModel method, fireTableDataChanged, to signal the table model’s lis-
teners when the table is changed; this causes the model’s view to update itself on the
display.
The crucial method in Figure 47, is getValueAt, which the JTable’s view repeat-
edly invokes to learn the contents of the cells in the grid it displays. For simplicity,
getValueAt can return a result of any object type whatsoever. The method is written
so that it returns candidates’ names, which are string objects, when asked about Col-
10.15. BEYOND THE BASICS 651
/** isCellEditable returns whether the user can change the value at Row i,
Column j by editing the display of that cell in the table’s view */
public boolean isCellEditable(int i, int j);
/** getColumnClass returns the data type of the objects held in Column j */
public Class getColumnClass(int j);
652
umn 0; when entries in other columns are requested, the integer vote count is fetched
and “wrapped” in a newly created Integer object. Think of getValueAt as a clever
“array look-up operation” that knows when to return names and when to return vote
counts to help the view display the table in Figure 45.
The table model is embedded in a JTable object and displayed by the application’s
frame, which is displayed along with the start-up class, in Figure 48.
Spreadsheets
For our purposes, a spreadsheet is a table that a user can edit by typing new in-
formation into the cells displayed in the table’s view. A JTable can be made into a
654
spreadsheet by improving its table model so that the model’s cells are “editable.” This
is done by writing new versions of the table model’s isCellEditable and setValueAt
methods.
For example, we can alter the vote table in Figure 49 so that a user can click on a
displayed cell and type a new number into it. When the user presses the Enter key or
clicks on a different cell, this causes the typed valued to replace the one that formerly
appeared in the cell. Figure 53 shows how a user is altering Row 2, Column 2 of the
table with a vote count of 1000; when the user presses Enter, this inserts the 1000
votes and all totals are revised accordingly.
Figure 54 shows how the table model is extended with methods that allow cell
editing. The isCellEditable method calculates exactly which cells of the table model
can be edited. (The cells that hold candidates’ names and the cells that display the
totals cannot be edited.) The setValueAt method takes the string that the user typed,
attempts to convert it into an integer and update the cell where the user typed it.
Badly formed integers are “trapped” by the exception handler.
area), we say that the mouse enters the component. Similarly, the mouse exits
the component when it is moved outside it.
• When a mouse’s button is pushed without moving the mouse, we say that the
mouse is clicked. It is possible to click the mouse multiple times at one click; a
double click is an example.
• When a mouse’s button is pushed and the mouse is moved while the button is
held, we say that the mouse is pressed. Eventually, the button is released.
/** getInputs returns the inputs the user selected and clears the frame
* @return (1) the index of the selected candidate;
* (2) the index of the selected state */
public int[] getInputs()
{ int[] input = new int[2];
input[0] = candidate list.getSelectedIndex();
candidate list.clearSelection();
input[1] = state list.getSelectedIndex();
state list.clearSelection();
return input;
}
}
10.15. BEYOND THE BASICS 659
{ super();
f.addMouseListener(this); // registers this object as a listener of f
}
new TestListener(v)
/** isCellEditable returns whether the cell at table row, table column
* can be edited by a user */
public boolean isCellEditable(int table row, int table column)
{ return ( table row < (super.getRowCount() - 1) // not a candidate’s total
&& table column > 0 // not a candidate’s name
&& table column < (super.getColumnCount() - 1) ); // not a region total
}
/** setValueAt attempts to insert new value into the cell at table row,
* table column. If new value is not an integer, it is ignored */
public void setValueAt(Object new value, int table row, int table column)
{ String s = (String)new value;
try { int i = new Integer(s).intValue();
changeVoteCount(table row, table column-1, i);
}
catch(RuntimeException e)
{ } // ignore the user’s clumsiness of typing a nonnumeric
}
}
/** mouseEntered handles the entry of a moving mouse into the component */
public void mouseEntered(MouseEvent e)
/** mouseExited handles the exit of a moving mouse from the component */
public void mouseExited(MouseEvent e)
/** mousePressed handles the mouse press that initiates a mouse drag */
public void mousePressed(MouseEvent e)
/** mouseReleased handles the mouse release that terminates a mouse drag */
public void mouseReleased(MouseEvent e)
}
Figure 57 shows a view class that has the methods for painting tiny boxes and yellow
ovals. The class’s paintComponent method clears the frame, meaning that moving or
closing/opening the frame makes it lose all previous drawings. If one wishes to retain
the drawings, then a model object is required for remembering the shapes and their
locations.
Figure 58 shows the controller class that translates the mouse clicks and drags
into drawings. The class contains a main method that tests the view and controller.
The mouseClicked method clears the frame on a double click; a single click draws
a box. For fun, the color (red or blue) is chosen based on the location of the click. A
mouse drag must be handled in two stages: mousePressed saves the position where the
drag starts, and mouseReleased retrieves this information and calculates the position
and size of the oval that the user indicated. Because the user might drag the mouse
from right to left and from bottom to top, Math.min and Math.abs are employed.
Remember that a mouse listener handles mouse events for only that graphical
component for which it is registered via addMouseListener. If the view object is a
frame with buttons, panels, text areas, etc., then each component requires its own
mouse listener to process mouse entries, exits, clicks, and presses. And, if a component
like a text area is embedded in a frame, then the text area’s mouse listener takes
precedence over the frame’s mouse listener when the mouse enters the text area.
The MouseListener interface in Figure 52 reports events related to the mouse’s
button. Events tied to mouse movement are described by the MouseMotionListener,
which is documented in the API for java.awt.event.
public MouseController(MouseView f)
{ view = f;
view.addMouseListener(this);
}
The interface, Runnable, promises that the class contains a method named run; this
is the method that will execute with its own thread.
Next, we modify the frame that displays the animations so that it shows two
panels of throbbing balls:
import java.awt.*;
import javax.swing.*;
public class Throb2Frame extends JFrame
{ public Throb2Frame(int size, ThrobPanel p1, ThrobPanel p2)
{ Container cp = getContentPane();
cp.setLayout(new GridLayout(1,2)); // display the animations side by side
cp.add(p1);
cp.add(p2);
setSize(size * 2 + 50, size + 10);
setVisible(true);
}
}
The important modifications occur in the start-up class, where each animation con-
troller is given its own thread of execution:
10.15. BEYOND THE BASICS 667
The phrase, new Thread(...).start(), gives an object its own thread of execution for
its run method, which immediately executes. In the above example, this causes the
two panels of animations to run simultaneously. Note also that the println statement
executes properly in main’s thread of execution after the two other threads are created
and running.
Threads can be used to build crude simulations where objects appear to have
“life” of their own. Consider a simulation of two sales agents, both of whom are
selling tickets to an opera. One agent works three times as fast as the other at selling
tickets. We might model the sales agents with objects that have their own threads of
execution; the objects share an object that holds the opera tickets. Here is the class
that models the opera tickets:
public class Tickets
{ private int how_many_left; // quantity of unsold tickets
The loop in the run method sells tickets until no more are available. The delay in
the loop is crucial, as explained below.
The start-up class creates threads for the two sales agents:
public class StartTicketSales
{ public static void main(String[] args)
{ Tickets c = new Tickets(20);
new Thread(new SalesAgent(1, c, 100)).start();
new Thread(new SalesAgent(2, c, 300)).start();
}
}
The application executes the two sales agents, which display their sales in the display.
As expected, the first sales agent sells three times as many tickets as the second.
The invocation of delay within the SalesAgent’s run method is crucial—on some
computers, the computer executes a thread for as long as possible, and only when
there is a delay in a thread’s execution will the computer resume execution of other
threads. Here, the delay method ensures that both of the sales agents have opportu-
nities to sell tickets.
10.15. BEYOND THE BASICS 669
Further, there is great danger when distinct threads of execution share an object.
To see this, move the invocation, delay(), within run’s loop to the beginning of the
loop:
while ( ticket_source.ticketsAvailable() )
{ delay(); // this causes erroneous behavior!
i = ticket_source.sellTicket();
System.out.println("Agent " + id + " sells ticket " + i);
}
Because the sales agent pauses between checking ticket availability and selling the
ticket, it is possible for two agents to both confirm availability of the last available
ticket and for both of them to sell it! This is indeed what happens when the above
example uses the altered loop.
The above problem is called a race condition (both agents are “racing” to sell the
last available ticket), and it is one of many problems that can arise when multiple
threads share an object. These problems cannot be analyzed further here. As a rule,
do not use the techniques in this section when the order in which a shared object
receives messages from multiple threads can affect the outcome of the application.
• Make it difficult or impossible for the user to generate a truly nonsensical se-
quence of events.
To help meet these guidelines, design a GUI so that any order of events generated from
a frame can be sensibly processed. If one event simply must occur before another,
then make the first event generate a secondary frame or dialog from which the second
event can occur. Also, consider all possible orders in which events might occur, and
write the controllers so that they respond accordingly. It also helps to design the
GUI so that if the user forgets to submit a particular input/event, a default value
is automatically supplied. When an input must be one of a finite number of choices
(e.g., “pick one: Celsius or Fahrenheit”), a component like a list of two items is better
than one like a text field.
The above advice is based on the designer knowing all the events a user might
generate—where does the designer get this knowledge? A standard technique for
cataloguing events is generating use-cases. A “use-case” is a description of the steps
670
that occur when a user submits one request to the application for computation—it is
a “case study” of one “use” of the application. By inventing a collection of use-cases,
a designer gains insight towards the design of the application’s architecture and its
GUI.
Here is an example of a use-case for a program that manages the balance in a
user’s bank account (see Chapter 6): A user deposits $10.50 into her account.
This use-case makes clear that the GUI must have a component that helps the user
submit a numerical amount and a component that helps the user select and perform
a deposit. There should be a model of the bank account that receives the deposit,
and there should be a GUI component that displays the new balance.
The above use-case does not make clear whether text fields, buttons, lists, or
labels should be used in the GUI—this is the decision of the designer. The nature of
the model is not decided, either. Additional use-cases are needed.
Here is another example of a use-case for the bank-account application:
4. The application reports that the withdrawal is refused because the account’s
balance is smaller than the withdrawal amount.
This use-case is concerned with an error situation, and it makes clear that the GUI
must have a means for reporting the error and helping the user correct the mistake.
A third use-case is:
This use-case reminds the designer that the application should deal with unexpected
orders of events and missing input information.
10.15. BEYOND THE BASICS 671
Based on such use-cases, the application’s designer compiles a list of the expected
behaviors (“methods”) of the application—deposits, withdrawals, account queries,
interest calculations, etc.—and the designer constructs a GUI that guides the user
through the operation of the application in a way that minimizes the potential for
invalid input. (For example, the GUI should make it difficult or impossible for a user
to demand a deposit or withdrawal without first specifying an amount.)
Here is a final thought to remember when you design an application’s GUI: Users
do not like to read instructions. Therefore, an interface should be organized to lead
a naive user through a useful execution of a program even when the user is mostly
uninformed about how to use the program. One way to help naive users is to limit
the possible activities a user might do with a frame. For example, if a frame consists
of just one button, the obvious (and only) action that a user could take is to push
the button. Of course, real interfaces are rarely so simple, but strive to minimize the
number of choices a user makes with any specific window. It is always better to lead
a user step-by-step through a program execution with multiple dialogs and frames
than it is to design one complex frame that triggers all the program’s methods.
If used judiciously, disabling buttons and menu items (through the use of the
setEnabled(false) method) can effectively limit a user’s actions. For example, a
text editor’s “paste” menu item can be disabled at the times the user has not cut
or copied text into the editor’s “clipboard.” But too much disabling and reenabling
buttons can confuse a user as to the state of the application.
The Tables that follow list the methods we used in this Chapter with AWT/Swing
components; they complement Figure 5. A more comprehensive listing can be located
in the API documentation for the packages java.awt, java.awt.event, javax.swing,
javax.swing.event, and javax.table.
672
abstract class Component the basic unit for GUI construction in the AWT
framework
Methods
setSize(int width, int See Table 18, Chapter 4.
depth), setVisible(boolean
yes), setLocation(int x, int
y), setBackground(Color c),
paint(Graphics g), repaint(),
getGraphics()
setForeground(Color c) Sets the foreground color of this object to c (if
such a notion is sensible for the object).
isShowing(): boolean Returns whether the component is showing on
the display.
getLocationOnScreen(): Returns the coordinates, encased in a Point ob-
Point ject, of (the upper left corner of ) the compo-
nent’s location on the display.
setLocation(Point p) Positions the component to appear at location
p on the display.
setFont(Font f) Sets the component’s text font to f.
classes Point and Font x,y coordinate positions and text fonts
Constructor
new Point(int x, int y) Constructs a point representing the pixel posi-
tion, x, y.
Methods
translate(int dx, int dy) Changes a point’s value so that its former posi-
tion, x, y, becomes x + dx, y + dy.
Constructor
new Font(String fontname, Creates a new font. fontname might be
int style, int typesize) "Courier", "TimesRoman", "SansSerif", and
others; style might be Font.PLAIN, Font.BOLD,
or Font.ITALIC; typesize is typically between 8
and 20.
10.15. BEYOND THE BASICS 673
class JFrame extends Window a window with a title and menu bar
Methods
setTitle(String s) See Table 18, Chapter 4.
getContentPane(): Container Returns the frame’s content pane, into which
components can be inserted.
setJMenuBar(JMenuBar mbar) Attaches menu bar mbar to the top of the frame.
abstract class JComponent the basic graphical object in the Swing frame-
extends Container work
class JTextField extends a text component that holds one line of text
JTextComponent
Constructor
JTextField(String s, int i) Constructs a text field that displays i columns
(that is, can display at most i copies of the
letter, “m”) whose initial value is s.
Methods
Registers listener as a listener (event handler)
addActionListener(ActionListener
listener) for action events (that is, presses of Enter) gen-
erated in this object.
class Observable an object that can generate its own “events” for
its Observers
Methods
addObserver(Observer ob) Registers ob as an Observer (“event handler”)
of this object.
setChanged() Asserts that this object has “changed” its inter-
nal state, causing an “event.”
notifyObservers() If this object has “changed,” (see setChanged),
then send an update message to this object’s
Observers. Next, reset the object so that it is
no longer “changed.”
notifyObservers(Object arg) If this object has “changed,” (see setChanged),
then send an update message to this object’s
Observers. Include with the message as its sec-
ond parameter, arg. Next, reset the object so
that it is no longer “changed.”
10.15. BEYOND THE BASICS 679
This picture should make clear why the fundamental operations on strings are length
and charAt. For example, s.length() returns 4, the length of the underlying array
of characters, and s.charAt(2) returns ’c’, because it is the character at element 2
of the underlying array.
Also, since strings are objects, it is no surprise that operations on strings are
written as method invocations, where the string comes first, followed by a dot, followed
by the operation (method) name, followed by arguments (if any), e.g., s.charAt(2).
At this point, you should study Table 5 of Chapter 3, which lists many of the methods
associated with class String.
It is possible to create a string directly from an array of characters:
674
The above statements create the string, "ab", and assign its address to t:
It is crucial that a separate object, a3, was created for u, because assignments to r’s
elements must not affect u. Also, a separate string is created for v. For this reason,
the expression, u == v, evaluates to false, because the two variables denote different
objects! In contrast, the equals method produces a different answer—u.equals(v)
computes to true (as does v.equals(u)), because equals compares the sequences of
characters within the two objects.
Perhaps the most important feature of String is this:
Java strings are immutable—once a String object is created, its contents cannot be
altered.
String s == a1 String u == a6
’a’ ’b’ ’c’ ’d’ ’e’ ’a’ ’b’ ’c’ ’d’ ’e’
then s’s cell would hold the address, a6, also. This is no disaster, because the object
at a6 cannot be altered—it stays constant at "abcde". For this reason, we need not
worry that character arrays underlie the strings we create, and we can pretend that
11.1. STRINGS ARE IMMUTABLE OBJECTS 675
strings are primitive values, just like numbers. We return to this illusion for the
remainder of the text.
(BeginFootnote: Java provides a version of strings that is mutable, that is, you
can assign to the elements of the string. It is called a StringBuffer and is found in
the package, java.lang. EndFootnote.)
With the assistance of the length and charAt methods, we can write loops that
check two strings for equality, extract a substring from a string, find one string inside
another, and so on. These are fundamental activities, and many of them are already
written as methods that belong to class String in java.lang. Table 9 in Chapter 3
lists some of the most useful of these methods.
Exercises
Using only the length, charAt, and + methods, write the following methods:
3. public String trimBlanks(String s) returns a string that looks like s but all
leading blanks and all trailing blanks are removed.
The string tokenizer object is constructed by the second statement, where the string
to be disassembled and the character that is the delimiter are supplied as the two
parameters. Here is a picture of the storage configuration:
a1 : StringTokenizer
String s == "Lucy MacGillicutty Ricardo"
To extract the first token from the string tokenizer object, we state,
String first_name = t.nextToken();
The nextToken method asks t to examine the string it holds, skip over leading de-
limiters (if any), and extract the maximal, nonempty sequence of characters until a
delimiter is encountered. In the example, this assigns "Lucy" to first name,
a1 : StringTokenizer
String s == "Lucy MacGillicutty Ricardo"
and we see that the text within the string tokenizer is shortened. Note that the
delimiter, the blank space, is left attached to the front of the text within the string
tokenizer.
When we extract the second token,
String middle_name = t.nextToken();
this skips over the leading blank, assigns "MacGillicutty" to middle name, and leaves
the object at a1 holding the string, " Ricardo". This assignment,
String last_name = t.nextToken();
assigns "Ricardo" to last name; the two leading blanks are discarded, and an empty
string remains in the string tokenizer. If we attempt to extract a fourth token, the
result is a NoSuchElementException, because no nonempty string can be returned.
Table 1 presents some of most useful methods for string tokenizers.
Here is a second example. Perhaps we wish to extract the tokens, 13 and 46 from
the string, $13.46. We do so most effectively with this sequence of statements:
11.1. STRINGS ARE IMMUTABLE OBJECTS 677
class StringTokenizer
Constructor
new StringTokenizer(String Constructs a string tokenizer which extracts tokens
text, String delim) from text, using as its separators all the characters
listed in string delim.
Methods
nextToken(): String Examine the text held in the tokenizer: Re-
move leading delimiters, and remove the maximal,
nonempty sequence of characters until a delimiter
or the end of the text is encountered. Return the
maximal, nonempty sequence of characters as the
result.
nextToken(String Like nextToken(), but first reset the delimiters
new delimiters): String used by the string tokenizer to the ones listed in
new delimiters.
hasMoreTokens(): boolean Return whether the text held in the string tokenizer
has any more tokens, based on the delimiters the
tokenizer currently holds.
countTokens(): int Return the number of tokens within the text held
in the string tokenizer, based on the delimiters the
tokenizer currently holds.
String s = "$13.46";
StringTokenizer t = new StringTokenizer(s, "$.");
String dollars = t.nextToken();
String cents = t.nextToken();
The second statement creates a string tokenizer that considers both $ and . to be legal
delimiters. A string tokenizer can use multiple distinct delimiters, and the delimiters
are packaged as a string to the tokenizer’s constructor method. The third statement
assigns "13" to dollars, because the leading symbol, $, is a delimiter and leading
delimiters are always skipped over. The "13" is extracted and the . is detected as a
delimiter. Next, cents is assigned 46, and the end of the string is encountered.
Exercises
1. What do each of the following display on the console?
(a) String x = "12,3,4";
StringTokenizer y = new StringTokenizer(x, ",");
String s = y.nextToken();
678
2. Revise Figure 3, Chapter 3, so that the MakeChange3 application reads its input
as follows,
and uses a string tokenizer to help assign values to variables dollars and cents.
3. Write an application that asks its user to type a complete sentence on one line.
The application then displays in the console window the words in the sentence,
one word per line, less any punctuation.
A file is indeed one long sequence; for example, these three text lines,
Hello to you!
How are you?
49
Every symbol, including the exact quantity of blanks, are faithfully reproduced in the
sequence. Newline characters (’\n’) mark the ends of lines. If you create a character
file with a PC, the MS-based operating system marks the end of each line with the
two-character sequence, \r\n, e.g.,
The examples in this chapter are written so that this difference is unimportant.
If you could see with your own eyes the characters saved on the magnetic disk,
you would not see letters like H and e. Instead, you would see that each character
is translated to a standard sized, numeric coding. One standard coding is ASCII
(pronounced “as-key”), which uses one byte to represent each character. Another
coding is Unicode, which uses two bytes to represent a character. (Unlike ASCII,
Unicode can represent accented and umlauted letters.) We will not concern ourselves
with which coding your computer uses to encode characters in a character file; these
details are hidden from us by the Java classes that read and write files.
A binary file is a sequence of ones and zeros. Binary files hold information that is
not keyboard-based. For example, if one wishes to save information about the address
numbers inside the computer’s primary storage, then a binary file is a good choice.
Also, purely numerical data is often stored in a binary file: Inside the computer, an
integer like 49 is used in its binary representation, 11001, and not in its character rep-
resentation (the characters ’4’ and ’9’). It is easier for the computer to do arithmetic
on binary representations, and programs that work extensively with files of numbers
work faster when the input and output data are saved in binary files—this saves the
trouble of converting from character codings to binary codings and back again.
The Java language even allows a program to copy objects in computer storage to
a binary file; this proves especially useful when one wants to archive the contents of
an executing program for later use.
When we obtain input information from a file, we say that we read from the file;
when we deposit output information into a file, we write to it. When we begin using
a file, we say that we open it, and when we are finished using the file, we close it. The
notions come from the old-fashioned concept of a filing cabinet whose drawer must
be opened to get the papers (“files”) inside and closed when no longer used. Indeed,
a computer must do a similar, internal “opening” of a disk file to see its contents,
and upon conclusion a “closing” of the disk file to prevent damage to its contents.
Files can be read/written in two ways:
• A sequential file must be read (or written) from front to back; it is like a book
whose pages can be turned in only one direction—forwards.
• A random access file allows reads (or writes) at any place within it; it is like a
normal book that can be opened to any particular page number.
680
Sequential files are simple to manage and work well for standard applications. Ran-
dom access files are used primarily for database applications, where specific bits of
information must be found and updated. In this chapter, we deal only with sequential
files.
When a Java program uses a sequential file, it must state whether the file is used
for
• input: the file is read sequentially and cannot be written
A FileWriter object holds the physical address of the named disk file and writes
individual characters to the file.
2. Next, we compose the FileWriter with a PrintWriter:
outfile.println("Hello to you!");
Appends the string, "Hello to you!", followed by a newline character, into file
test.txt.
11.2. SEQUENTIAL FILES 681
Figure 2 displays an example application that writes three lines of characters into
the sequential file named test.txt. The application’s output-view object is outfile,
and it can be constructed in just one statement as
PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));
When the FileWriter object is constructed, it locates and opens the file, "test.txt",
in the local directory (folder); if the file does not exist, a new, empty file with the
name is created and opened. Files in other directories can be similarly opened pro-
vided a correct path name is specified, e.g., "C:\\Fred\\DataFiles\\test.txt" on an
MS-based file system or "/home/Fred/DataFiles/test.txt" on a Unix-based system.
(Recall that we must use \\ to stand for one backslash in a Java string.)
The output-view object possesses methods, println and print, for writing strings
and numbers, and a method, close, for closing a file. (As always, the println method
appends a newline character, ’\n’, to the end of the string that it writes.)
The program in Figure 2 writes three lines of text to test.txt, closes the file, and
terminates. You can read the contents of test.txt with a text editor, and you will
see
Hello to you!
How are you?
49
The above code fragment creates two objects that write information to two files,
test.txt and AnotherFile.out.
Exercises
1. Write an application that asks its user to type multiple lines of text into the con-
sole window; the application copies the lines the user types into a file, text.txt.
2. Modify the change-making program in Figure 2, Chapter 4, so that the pro-
gram’s output is displayed in a console window as is written to a file, change.out.
3. Modify class DialogReader in Figure 11, Chapter 5, so that it maintains a
“log”: Every message displayed in a dialog and every string typed by the user
is copied to a file, dialog.log.
This constructs a FileReader object that holds the file’s physical address and
can read individual characters from the file. (If the file cannot be found, new
FileReader("test.txt") generates an input-output exception.)
The BufferedReader object uses the FileReader object to collect a full line
of characters, which it forms into a string. The message, infile.readLine(),
returns the string as its result.
Exercises
1. Create a file, ints.dat, which holds one integer per line. Write an application,
class Sum, which reads ints.dat and prints the sum of the integers in the file.
2. Modify CopyFile in Figure 3 so that when it reads a line of text from the input
file, it writes the words in the line to the output file, one word per output line.
(Hint: use a string tokenizer.)
3. Modify class VoteCount in Figure 1, Chapter 8, so that the application reads its
votes from a file, votes.dat. (Hint: replace DialogReader with a new input-view
class, class VoteReader, whose readInt method reads a vote from votes.dat.)
The user interacts with the application by typing at the keyboard, say, 3, and pressing
the newline key; almost immediately, she sees another prompt:
Perhaps the user types 46 and newline. The answer then appears within the same
686
window:
new InputStreamReader(System.in)
Like the BufferedReader objects seen earlier, this object has a method named
readLine that can read one full line of input.
The input behavior we saw in the earlier demonstration is caused by these state-
ments,
The first statement prints a partial line, which signals the user to type the dollars
amount, and the second statement grabs what the user types. The third and fourth
statements act similarly.
Here is the modified version of the change-making program:
import java.io.*;
/** MakeChange2 calculates change for a dollars-and-cents amount.
* input: a dollars amount and a cents amount, both integers
* output: the listing of the needed coins */
public class MakeChange2
{ public static void main(String[] args) throws IOException
{ BufferedReader keyboard
= new BufferedReader(new InputStreamReader(System.in));
Fred Mertz|31|20.25
Lucy Ricardo|42|24.50
Ethel Mertz|18|18.00
!
688
class PayrollReader
Methods
getNextRecord(): boolean Attempt to read the next payroll record from the
input file. Returns whether or not a properly for-
matted record was successfully read.
nameOf(): String Return the name of the employee of the current
payroll record.
hoursOf(): int Return the hours worked of the employee of the
current payroll record.
payrateOf(): double Return the payrate of the employee of the current
payroll record.
close() Close the payroll file.
/** PayrollReader constructs the reader to read from file file name */
public PayrollReader(String file name)
{ try { infile = new BufferedReader(new FileReader(file name)); }
catch (Exception e)
{ System.out.println("PayrollReader error: bad file name: "
+ file name + " Aborting!");
throw new RuntimeException(e.toString());
}
}
These techniques with exceptions are examined in the sections that follow.
Exercise
Specify and write class PayrollWriter for the payroll example.
Testing reveals that this program generates an exception when the user types a 0
for the integer input; we see in the command window this message:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at DivideIntoTwelve.main(DivideIntoTwelve.java:0)
A similar situation occurs if the user mistakenly types a non-integer, say, the text,
one:
at java.lang.Integer.parseInt(Integer.java:405)
at java.lang.Integer.<init>(Integer.java:540)
at DivideIntoTwelve.readAnIntFrom(DivideIntoTwelve.java:14)
at DivideIntoTwelve.main(DivideIntoTwelve.java:6)
Exception handlers are wordy—the try section brackets the questionable statement,
and the catch section lists the statements that execute if an exception arises.
The statement in the try section is executed first; if i is nonzero (e.g., 2), the divi-
sion proceeds without error, the dialog displaying the answer appears, and Finished.
prints in the command window.
If i has value 0, however, then the division is stopped by a runtime exception—
we say that an exception is thrown. At this moment, the statements in the catch
section of the exception handler execute, displaying a dialog—the exception has been
handled. When the exception handler completes its work, the program executes the
statements that follow, as if nothing wrong had happened. (Here, Finished. prints.)
The phrase, RuntimeException e, documents the form of exception that can be
caught—here, it is a runtime exception. (The purpose of the e, which is actually a
formal parameter, is explained in Chapter 11.)
The general format of Java’s exception handler construction goes
try { STATEMENTS }
catch ( EXCEPTION )
{ HANDLER }
694
where STATEMENTS and HANDLER are sequences of zero or more statements. The EXCEPTION
phrase is normally just RuntimeException e, but Chapter 11 documents other forms.
When executed, the STATEMENTS part begins. If no exceptions are thrown, then
the catch part is ignored. But if an exception is thrown, and if it has the form named
EXCEPTION, then the statements, HANDLER, are executed.
Exercises
1. Given the main method with its exception handler, how does the program behave
when the user inputs the text, zero, as the input?
2. Insert an exception handler into the private method, readAnInt, so that a dia-
log appears with the phrase, Error: Noninteger Input, when a non-integer is
supplied as input.
3. Insert an exception handler into Figure 5 so that if the user supplies a non-
integer as input, the program responds with a dialog that complains about the
invalid input and quits without printing an answer.
Say that a user types four for the initial input; the execution configuration looks
like this:
DivideIntoTwelve
main
{ 1> int i = AWAIT RESULT FROM readAnInt;
...
}
readAnInt
{ int num == ?
String s == "four"
DivideIntoTwelve
main
{ 1> int i = AWAIT RESULT FROM readAnInt;
...
}
readAnInt
{ int num == ?
String s == "four"
try{ ... }
catch( ... )
{ ... // dialog anniunces the error
2> num = readAnInt();
}
return num;
}
The restarted method asks the user for a fresh input. If the user types, say, 4, the
696
String s == "four"
try{ ... }
catch( ... )
{ ... // dialog anniunces the error
2> num = AWAIT RESULT FROM readAnInt();
}
return num;
}
readAnInt
{ 3> int num;
...
return num;
}
The restarted copy of readAnInt uses its own local variables to compute the integer,
4, that is returned to the original invocation of readAnInt:
DivideIntoTwelve
main
{ 1> int i = AWAIT RESULT FROM readAnInt;
...
}
readAnInt
{ int num == ?
String s == "four"
try{ ... }
catch( ... )
{ ... // dialog anniunces the error
2> num = AWAIT RESULT FROM readAnInt();
}
return num;
}
readAnInt
{ int num == 4
...
String s == "4"
...
3> return num;
}
The exception handler in the initial invocation of readAnInit finishes its repair by
assigning 4 to num, which is returned to the awaiting main method.
This example shows how an exception handler can repair a truly difficult situation
so that a program can proceed to its conclusion. In this and subsequent chapters, we
11.5. EXCEPTIONS AND EXCEPTION HANDLERS 697
will use recursive invocations to restart methods; Chapters 7 and 12 present other
uses for recursive invocations.
And, if the user typed an incorrect input, the statements would throw an exception
and terminate the program.
It would be better to have a standardized input-view that had skillful methods for
fetching integers, doubles, and strings from the user and helping the user if an input
was incorrectly typed. Once we had such an input view, we could use it as follows:
Better still, we could reuse this same input-view, DialogReader, for all the applications
we write that require input.
Table 7 states the interface we shall use for the input-view class.
As an example, we can change the application in Figure 5, Chapter 5, to work
with the interface in Table 7. This simplifies its main method to the following:
Next, we write a class that uses JOptionPane and exception handlers to match the
interface. See Figure 8.
Method readString reads a line of text from the dialog, using the standard tech-
nique.
The readInt method uses readString to fetch the user’s input, which must be
converted from a string, s, into an integer in the standard way:
There is a small novelty: The string method, trim(), removes the leading and trail-
ing blanks from the string before it is given to the Integer helper object for con-
version. (Recall from Chapter 3 that string operations are written in the format,
S.op(args), for a string, S, operation, op, and arguments (if any), args.) For example,
if readString(prompt) returned the string, " 123 ", then " 123 ".trim() computes
to "123".
If the user typed a non-integer, then the attempt to convert it into an integer
would throw a runtime exception. Fortunately, the exception handler handles the
exception by restarting the readInt method, just like we saw in the previous section.
Method readDouble works similarly to readInt.
• a description of where the error occurred, namely, the statement number, method,
and class, and a history of all the method invocations that led to the statement.
These pieces of information are packaged into a newly constructed exception object,
which becomes the “result” of the erroneous computation. This result object seeks its
its handler, which is usually the IDE or JDK that started the program’s execution.
The handler displays the object’s contents on the console window.
11.6. EXCEPTIONS ARE OBJECTS 699
There are many classes from which exception objects can be built, e.g., class
ArrayIndexOutOfBoundsException and class NullPointerException are two exam-
ples; both are found in the java.lang package. Figure 9 gives a partial listing of the
exception hierarchy; no doubt, many of its class names are familiar.
Exception objects have several useful methods, presented in Table 10 The methods
extract the information embedded within an exception. We use them momentarily.
As noted in Figure 9, the exceptions we have encountered fall into two groups: run-
time exceptions and input-output exceptions. Runtime exceptions are subtle errors
that are a fact of programming life. Since they can occur at almost every statement,
the Java compiler assumes that every method in a program “throws RuntimeEx-
ception.” In contrast, input-output exceptions are errors that might arise due to a
serious problem with file usage. Such an error is uncommon, and the Java compiler
requires that every method which might generate the error be labelled with throws
IOException.
The usual outcome of an error is the creation of an exception object that finds its
way to its default handler, the IDE or JDK. But in some cases, a programmer might
prefer that the exception object find its way to a different handler, one that does not
terminate execution—the programmer writes an exception handler to “catch” and
“handle” exception objects.
The format of an exception handler goes
try { STATEMENTS }
catch (EXCEPTION_TYPE e)
{ HANDLER }
getMessage(): String Return the message that describes the error that
occurred
toString(): String Return a string representation of the exception ob-
ject; this is usually the class name of the exception
and the message that describes the error.
printStackTrace() Print on the console window the statement number,
method, and class where the error occurred as well
as all the method invocations that led to it.
String s = view.readLine();
try { num = new Integer(s).intValue(); }
catch (RuntimeException e)
{ System.out.println("Non-integer error");
num = -1;
}
return num;
}
import java.io.*;
public class ExceptionExample
{ public ExceptionExample() { }
}
}
it is better for a programmer to invest her time towards preventing errors with if-
statements rather than recovering from errors with exception handlers.
is always preferred to
Exercises
1. It is usually disastrous for a program to terminate while reading or writing a
file. Here is a program that reads integers from a file and squares them:
import java.io.*;
public class Squares
{ public static void main(String[] args) throws IOException
{ BufferedReader infile = new BufferedReader(new FileReader("ints.dat"));
while ( infile.ready() )
{ int i = new Integer(infile.readLine().trim()).intValue();
System.out.println(i + " squared is " + (i*i));
}
infile.close();
}
}
Revise this class so that an invalid input line causes the program to display
Illegal input---skipping to next line and causes the program to proceed
to the next line of input.
11.7 Summary
We summarize the main points of the chapter.
New Construction
• exception handler:
New Terminology
• token: a “word” within a string or a line of text
• sequential file: a file that is a sequence of symbols. A sequential file can be read
(or written) only from front to back, like a book whose pages can be turned
only forwards. Two forms of sequential file are
• random-access file: a file that can be read or written in any order at all.
• ASCII and Unicoding: two coding formats for representing characters saved
within a file exception: an error that occurs during program execution (e.g.,
executing 12/0 throws a division-by-zero exception). Normally, an exception
halts a program unless an exception handler construction is present to handle
(repair) the exception (see the exception handler in readString above, which
handles an input-failure exception).
Points to Remember
Here are some notions regarding files to keep in mind:
One uses the print and println methods of the object to write text to the file.
An input file is easily created from a BufferedReader object, e.g.,
One uses the readLine method to read one line of text from the file.
• Errors that might arise during file usage are called input-output exceptions, and
a method in which an input-output exception might arise must be labelled with
throws IOException.
Exceptions are in fact objects, and a programmer can write an exception han-
dler, try ... catch, to “catch” and “handle” an exception.
(b) Next, modify PrettyPrint so that (i) hyphenated words and phrases con-
nected by dashes can be broken across lines; (ii) two blanks, rather than
one, are placed after a sentence-ending period.
(c) Finally, modify PrettyPrint so that the output lines are right justified.
2. Write a file merge program: It reads as its input two files of integers (one integer
per line), where the integers in each file are sorted in nondescending order. The
output is a new file, containing the contents of the two input files, of the integers
sorted in nondescending order.
3. Write a program that reads a sequential file and calculates the average length
of its words, its shortest nonnull word and its longest word. (Here, a “word” is
a sequence of non-blank, non-tab, non-newline characters. One text line might
contain multiple words.)
4. Write a program that reads a sequential file of integers and calculates the mean
of the integers, the maximum and minimum integers, and the standard deviation
of the integers, where mean and standard deviation are defined as
M ean = (n0 + n1 + ...nm )/m
q
StdDev = −(M ean2 ) + (n0 )2 + (n1 )2 + ... + (nm )2
where the input file contains the m integers, n 0, n 1, ..., n m. (Recall the standard
deviation tells us that two-thirds of the integers in the file fall in the range (Mean
- StdDev) .. (Mean + StdDev).)
(c) Next, modify the program to count occurrences of distinct words only, that
is, multiple appearances of the word, “the”, in the file count for only one
occurrence of a three-letter word.
710
6. Write a program that reads a Java program, removes all /* ... */ and // ...
comments, and leaves the program otherwise unaltered.
(a) The input is a sequential text file, where sequences of n repeating charac-
ters, aaa...a, of length 4 to 9 are compressed into \na For example, the
line
Thisssss is aaaa testtt 22225.
is compressed to
Thi\5s is \4a\6 testtt \425.
8. Write a lexical analyzer for numerical and logical expressions: The program’s
input is an arithmetic expression containing numerals, parentheses, and the
operators +, -, *, /, <, <=, &&, and !. The output is a file of integers, where the
input is translated as follows:
For example, the input ( 2+34 <=(-5+6)) && !(789==0)) translates to the out-
put sequence 1 0 2 3 0 34 8 1 4 0 5 2 0 6 2 2 9 10 1 0 789 6 0 0 2 2.
9. Write a checkbook accountant program. The input is sequential file that be-
gins with the account’s initial balance, followed by a sequence of deposit and
withdrawal transactions. The output is a history of the account, showing each
transaction and the current balance after the transaction.
10. Write a program that reads a file of persons’ names and addresses and a file
that contains a “form letter” (e.g., a letter asking the recipient to subscribe to
a magazine) and creates for its output a collection of output files, one output
file for each person’s name, where each output file contains the form letter
personalized to the person’s name.
11.9. BEYOND THE BASICS 711
int c = infile.read();
char d = (char)c;
String s = "abc" + d;
In a similar way, one can read and write integers and booleans.
As the name suggests, entire objects can be written to an ObjectOutputStream
and read from an ObjectInputStream. Figure 12 displays a program that writes an
integer and several objects to a binary file and then reads the file and restores the
objects.
11.9. BEYOND THE BASICS 713
Objects are written to files with the writeObject method. Of course, objects often
have fields that hold addresses of other objects, as is the case with ComposedCell c in
the Figure. So, writeObject(c) saves not only c’s object but also the objects whose
addresses are held by c’s fields, x and y. An integer and a Cell object are also written,
to show that different types of values can be mixed in the binary file.
When the file test.ob is reopened as an input file, the file’s contents must be read
in the same order in which it was written. This means the ComposedCell object must
be read first:
Object ob = in.readObject();
ComposedCell m = (ComposedCell)ob; // cast ob into a ComposedCell
The readObject method returns a result of type object; this object must be cast into
type ComposedCell, and at this point, the object is restored. The integer and Cell
object are read next.
Both classes ComposedCell and Cell are labelled implements Serializable to as-
sert that objects created from the classes can be saved in sequential binary files. (The
Java compiler verifies that a “serializable” class contains only fields that can be safely
copied onto files.) Also, a method that reads a binary file of objects must state throws
ClassNotFoundException to warn of the possibility that an object of unknown data
type (class) might be read.
• A program rarely computes upon bytes directly; units like characters (or integers
or objects) are read and written. Therefore, a stream must be composed with
an object whose methods read/write bytes and collect them into characters,
integers, objects, etc. This builds character streams, object streams, etc.
• Objects that manipulate character streams are called readers and writers. A
reader or writer might be buffered, meaning that it collects characters into
strings and reads/writes strings.
Here are some examples of objects for input and output. To assemble an object
that reads strings (lines of characters) from a file, data.in, we might say
BufferedReader infile = new BufferedReader
(new InputStreamReader(new FileInputStream("data.in"));
and this is the pattern used in this chapter to build most input-file objects. Also, the
declaration,
FileReader in = new FileReader("data.in");
creates a reader from which we can read characters one at a time. (But the reader is
not buffered.)
In a similar way,
PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));
creates a buffered writer that uses a writer, new FileWriter("file"). The latter ab-
breviates the character output stream, new OutputStreamReader(new FileOutputStream
("test.txt")).
We create a file to which objects are written with this declaration:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d.ob"));
The file chooser lets a user click on a folder to open it and click on a file name to
select a file. When the user presses the Select button, the path name of the selected
file is remembered, and the program that created the file chooser can retrieve the
path name.
A simple way to create and show a file chooser is
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Choose a file");
int result = chooser.showDialog(null, "Select");
The resulting dialog displays the contents of the folder (directory) of the user’s “home”
folder; the dialog’s title is set by setDialogTitle. After the user selects a file and
presses the button labelled Select, the selected file’s path name is retrieved and used
as follows:
if ( result == JFileChooser.APPROVE_OPTION )
11.9. BEYOND THE BASICS 723
class JFileChooser
Constructors
JFileChooser() Constructs a file chooser which points to the con-
tents of the user’s home folder (directory)
JFileChooser(String Constructs a file chooser which points to the con-
path name) tents of the folder path name.
JFileChooser(File path name) Constructs a file chooser which points to the con-
tents of the folder path name.
Methods
getSelectedFile(): File Return the file selected in the file chooser’s view
setDialogTitle(String title) Set the title of the file chooser’s dialog to title
{ File f = chooser.getSelectedFile();
String path_name = f.getPath();
... // use path_name to create a file object
}
else { System.out.println
("Error: Select button was not pushed or pushed with no file selected");
}
The file chooser returns an integer code which equals JFileChooser.APPROVE OPTION if
the user selected a file (or typed a file name into the dialog’s text area) and terminated
the dialog by pushing the Select button (or pressing Enter in the text area). The
getSelectedFile method returns a File object from which one extracts the path
name with getPath.
Figure 14 shows some of the methods for class JFileChooser.
Exercise
Extend the text editor in Figure 33, Chapter 10 so that it has a menu item that reads
a file into the editor’s text area and one that saves the contents of the text area into
a file.
Chapter 11
This picture should make clear why the fundamental operations on strings are length
and charAt. For example, s.length() returns 4, the length of the underlying array
of characters, and s.charAt(2) returns ’c’, because it is the character at element 2
of the underlying array.
Also, since strings are objects, it is no surprise that operations on strings are
written as method invocations, where the string comes first, followed by a dot, followed
by the operation (method) name, followed by arguments (if any), e.g., s.charAt(2).
At this point, you should study Table 5 of Chapter 3, which lists many of the methods
associated with class String.
It is possible to create a string directly from an array of characters:
682
The above statements create the string, "ab", and assign its address to t:
It is crucial that a separate object, a3, was created for u, because assignments to r’s
elements must not affect u. Also, a separate string is created for v. For this reason,
the expression, u == v, evaluates to false, because the two variables denote different
objects! In contrast, the equals method produces a different answer—u.equals(v)
computes to true (as does v.equals(u)), because equals compares the sequences of
characters within the two objects.
Perhaps the most important feature of String is this:
Java strings are immutable—once a String object is created, its contents cannot be
altered.
String s == a1 String u == a6
’a’ ’b’ ’c’ ’d’ ’e’ ’a’ ’b’ ’c’ ’d’ ’e’
then s’s cell would hold the address, a6, also. This is no disaster, because the object
at a6 cannot be altered—it stays constant at "abcde". For this reason, we need not
worry that character arrays underlie the strings we create, and we can pretend that
11.1. STRINGS ARE IMMUTABLE OBJECTS 683
strings are primitive values, just like numbers. We return to this illusion for the
remainder of the text.
(BeginFootnote: Java provides a version of strings that is mutable, that is, you
can assign to the elements of the string. It is called a StringBuffer and is found in
the package, java.lang. EndFootnote.)
With the assistance of the length and charAt methods, we can write loops that
check two strings for equality, extract a substring from a string, find one string inside
another, and so on. These are fundamental activities, and many of them are already
written as methods that belong to class String in java.lang. Table 9 in Chapter 3
lists some of the most useful of these methods.
Exercises
Using only the length, charAt, and + methods, write the following methods:
3. public String trimBlanks(String s) returns a string that looks like s but all
leading blanks and all trailing blanks are removed.
The string tokenizer object is constructed by the second statement, where the string
to be disassembled and the character that is the delimiter are supplied as the two
parameters. Here is a picture of the storage configuration:
a1 : StringTokenizer
String s == "Lucy MacGillicutty Ricardo"
To extract the first token from the string tokenizer object, we state,
String first_name = t.nextToken();
The nextToken method asks t to examine the string it holds, skip over leading de-
limiters (if any), and extract the maximal, nonempty sequence of characters until a
delimiter is encountered. In the example, this assigns "Lucy" to first name,
a1 : StringTokenizer
String s == "Lucy MacGillicutty Ricardo"
and we see that the text within the string tokenizer is shortened. Note that the
delimiter, the blank space, is left attached to the front of the text within the string
tokenizer.
When we extract the second token,
String middle_name = t.nextToken();
this skips over the leading blank, assigns "MacGillicutty" to middle name, and leaves
the object at a1 holding the string, " Ricardo". This assignment,
String last_name = t.nextToken();
assigns "Ricardo" to last name; the two leading blanks are discarded, and an empty
string remains in the string tokenizer. If we attempt to extract a fourth token, the
result is a NoSuchElementException, because no nonempty string can be returned.
Table 1 presents some of most useful methods for string tokenizers.
Here is a second example. Perhaps we wish to extract the tokens, 13 and 46 from
the string, $13.46. We do so most effectively with this sequence of statements:
11.1. STRINGS ARE IMMUTABLE OBJECTS 685
class StringTokenizer
Constructor
new StringTokenizer(String Constructs a string tokenizer which extracts tokens
text, String delim) from text, using as its separators all the characters
listed in string delim.
Methods
nextToken(): String Examine the text held in the tokenizer: Re-
move leading delimiters, and remove the maximal,
nonempty sequence of characters until a delimiter
or the end of the text is encountered. Return the
maximal, nonempty sequence of characters as the
result.
nextToken(String Like nextToken(), but first reset the delimiters
new delimiters): String used by the string tokenizer to the ones listed in
new delimiters.
hasMoreTokens(): boolean Return whether the text held in the string tokenizer
has any more tokens, based on the delimiters the
tokenizer currently holds.
countTokens(): int Return the number of tokens within the text held
in the string tokenizer, based on the delimiters the
tokenizer currently holds.
String s = "$13.46";
StringTokenizer t = new StringTokenizer(s, "$.");
String dollars = t.nextToken();
String cents = t.nextToken();
The second statement creates a string tokenizer that considers both $ and . to be legal
delimiters. A string tokenizer can use multiple distinct delimiters, and the delimiters
are packaged as a string to the tokenizer’s constructor method. The third statement
assigns "13" to dollars, because the leading symbol, $, is a delimiter and leading
delimiters are always skipped over. The "13" is extracted and the . is detected as a
delimiter. Next, cents is assigned 46, and the end of the string is encountered.
Exercises
1. What do each of the following display on the console?
(a) String x = "12,3,4";
StringTokenizer y = new StringTokenizer(x, ",");
String s = y.nextToken();
686
2. Revise Figure 3, Chapter 3, so that the MakeChange reads its input as follows
String input =
JOptionPane.showInputDialog("Type a dollars, cents amount for change: $:");
and uses a string tokenizer to assign values to variables dollars and cents.
3. Write an application that asks its user to type a complete sentence on one line.
The application then displays in the console window the words in the sentence,
one word per line, less any punctuation.
A file is indeed one long sequence; for example, these three text lines,
Hello to you!
How are you?
49
Every symbol, including the exact quantity of blanks, are faithfully reproduced in the
sequence. Newline characters (’\n’) mark the ends of lines. If you create a character
file with a PC, the MS-based operating system marks the end of each line with the
two-character sequence, \r\n, e.g.,
The examples in this chapter are written so that this difference is unimportant.
If you could see with your own eyes the characters saved on the magnetic disk,
you would not see letters like H and e. Instead, you would see that each character
is translated to a standard sized, numeric coding. One standard coding is ASCII
(pronounced “as-key”), which uses one byte to represent each character. Another
coding is Unicode, which uses two bytes to represent a character. (Unlike ASCII,
Unicode can represent accented and umlauted letters.) We will not concern ourselves
with which coding your computer uses to encode characters in a character file; these
details are hidden from us by the Java classes that read and write files.
A binary file is a sequence of ones and zeros. Binary files hold information that is
not keyboard-based. For example, if one wishes to save information about the address
numbers inside the computer’s primary storage, then a binary file is a good choice.
Also, purely numerical data is often stored in a binary file: Inside the computer, an
integer like 49 is used in its binary representation, 11001, and not in its character rep-
resentation (the characters ’4’ and ’9’). It is easier for the computer to do arithmetic
on binary representations, and programs that work extensively with files of numbers
work faster when the input and output data are saved in binary files—this saves the
trouble of converting from character codings to binary codings and back again.
The Java language even allows a program to copy objects in computer storage to
a binary file; this proves especially useful when one wants to archive the contents of
an executing program for later use.
When we obtain input information from a file, we say that we read from the file;
when we deposit output information into a file, we write to it. When we begin using
a file, we say that we open it, and when we are finished using the file, we close it. The
notions come from the old-fashioned concept of a filing cabinet whose drawer must
be opened to get the papers (“files”) inside and closed when no longer used. Indeed,
a computer must do a similar, internal “opening” of a disk file to see its contents,
and upon conclusion a “closing” of the disk file to prevent damage to its contents.
Files can be read/written in two ways:
• A sequential file must be read (or written) from front to back; it is like a book
whose pages can be turned in only one direction—forwards.
• A random access file allows reads (or writes) at any place within it; it is like a
normal book that can be opened to any particular page number.
688
Sequential files are simple to manage and work well for standard applications. Ran-
dom access files are used primarily for database applications, where specific bits of
information must be found and updated. In this chapter, we deal only with sequential
files.
When a Java program uses a sequential file, it must state whether the file is used
for
• input: the file is read sequentially and cannot be written
A FileWriter object holds the physical address of the named disk file and writes
individual characters to the file.
2. Next, we compose the FileWriter with a PrintWriter:
outfile.println("Hello to you!");
Appends the string, "Hello to you!", followed by a newline character, into file
test.txt.
11.2. SEQUENTIAL FILES 689
Figure 2 displays an example application that writes three lines of characters into
the sequential file named test.txt. The application’s output-view object is outfile,
and it can be constructed in just one statement as
PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));
When the FileWriter object is constructed, it locates and opens the file, "test.txt",
in the local directory (folder); if the file does not exist, a new, empty file with the
name is created and opened. Files in other directories can be similarly opened pro-
vided a correct path name is specified, e.g., "C:\\Fred\\DataFiles\\test.txt" on an
MS-based file system or "/home/Fred/DataFiles/test.txt" on a Unix-based system.
(Recall that we must use \\ to stand for one backslash in a Java string.)
The output-view object possesses methods, println and print, for writing strings
and numbers, and a method, close, for closing a file. (As always, the println method
appends a newline character, ’\n’, to the end of the string that it writes.)
The program in Figure 2 writes three lines of text to test.txt, closes the file, and
terminates. You can read the contents of test.txt with a text editor, and you will
see
Hello to you!
How are you?
49
The above code fragment creates two objects that write information to two files,
test.txt and AnotherFile.out.
Exercises
1. Write an application that asks its user to type multiple lines of text into the con-
sole window; the application copies the lines the user types into a file, text.txt.
This constructs a FileReader object that holds the file’s physical address and
can read individual characters from the file. (If the file cannot be found, new
FileReader("test.txt") generates an input-output exception.)
The BufferedReader object uses the FileReader object to collect a full line
of characters, which it forms into a string. The message, infile.readLine(),
returns the string as its result.
at java.io.FileReader.<init>(...)
at CopyFile.main(CopyFile.java:10)
Exercises
1. Create a file, ints.dat, which holds one integer per line. Write an application,
class Sum, which reads ints.dat and prints the sum of the integers in the file.
2. Modify CopyFile in Figure 3 so that when it reads a line of text from the input
file, it writes the words in the line to the output file, one word per output line.
(Hint: use a string tokenizer.)
The user interacts with the application by typing at the keyboard, say, 3, and pressing
the newline key; almost immediately, she sees another prompt:
Perhaps the user types 46 and newline. The answer then appears within the same
694
window:
new InputStreamReader(System.in)
Like the BufferedReader objects seen earlier, this object has a method named
readLine that can read one full line of input.
The input behavior we saw in the earlier demonstration is caused by these state-
ments,
The first statement prints a partial line, which signals the user to type the dollars
amount, and the second statement grabs what the user types. The third and fourth
statements act similarly.
Here is the modified version of the change-making program:
import java.io.*;
/** MakeChange2 calculates change for a dollars-and-cents amount.
* input: a dollars amount and a cents amount, both integers
* output: the listing of the needed coins */
public class MakeChange2
{ public static void main(String[] args) throws IOException
{ BufferedReader keyboard
= new BufferedReader(new InputStreamReader(System.in));
Fred Mertz|31|20.25
Lucy Ricardo|42|24.50
Ethel Mertz|18|18.00
!
696
class PayrollReader
Methods
getNextRecord(): boolean Attempt to read the next payroll record from the
input file. Returns whether or not a properly for-
matted record was successfully read.
nameOf(): String Return the name of the employee of the current
payroll record.
hoursOf(): int Return the hours worked of the employee of the
current payroll record.
payrateOf(): double Return the payrate of the employee of the current
payroll record.
close() Close the payroll file.
/** PayrollReader constructs the reader to read from file file name */
public PayrollReader(String file name)
{ try { infile = new BufferedReader(new FileReader(file name)); }
catch (Exception e)
{ System.out.println("PayrollReader error: bad file name: "
+ file name + " Aborting!");
throw new RuntimeException(e.toString());
}
}
These techniques with exceptions are examined in the sections that follow.
Exercise
Specify and write class PayrollWriter for the payroll example.
Testing reveals that this program generates an exception when the user types a 0
for the integer input; we see in the command window this message:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at DivideIntoTwelve.main(DivideIntoTwelve.java:0)
A similar situation occurs if the user mistakenly types a non-integer, say, the text,
one:
at java.lang.Integer.parseInt(Integer.java:405)
at java.lang.Integer.<init>(Integer.java:540)
at DivideIntoTwelve.readAnIntFrom(DivideIntoTwelve.java:14)
at DivideIntoTwelve.main(DivideIntoTwelve.java:6)
Exception handlers are wordy—the try section brackets the questionable statement,
and the catch section lists the statements that execute if an exception arises.
The statement in the try section is executed first; if i is nonzero (e.g., 2), the divi-
sion proceeds without error, the dialog displaying the answer appears, and Finished.
prints in the command window.
If i has value 0, however, then the division is stopped by a runtime exception—
we say that an exception is thrown. At this moment, the statements in the catch
section of the exception handler execute, displaying a dialog—the exception has been
handled. When the exception handler completes its work, the program executes the
statements that follow, as if nothing wrong had happened. (Here, Finished. prints.)
The phrase, RuntimeException e, documents the form of exception that can be
caught—here, it is a runtime exception. (The purpose of the e, which is actually a
formal parameter, is explained in Chapter 11.)
The general format of Java’s exception handler construction goes
try { STATEMENTS }
catch ( EXCEPTION )
{ HANDLER }
702
where STATEMENTS and HANDLER are sequences of zero or more statements. The EXCEPTION
phrase is normally just RuntimeException e, but Chapter 11 documents other forms.
When executed, the STATEMENTS part begins. If no exceptions are thrown, then
the catch part is ignored. But if an exception is thrown, and if it has the form named
EXCEPTION, then the statements, HANDLER, are executed.
Exercises
1. Given the main method with its exception handler, how does the program behave
when the user inputs the text, zero, as the input?
2. Insert an exception handler into the private method, readAnInt, so that a dia-
log appears with the phrase, Error: Noninteger Input, when a non-integer is
supplied as input.
3. Insert an exception handler into Figure 5 so that if the user supplies a non-
integer as input, the program responds with a dialog that complains about the
invalid input and quits without printing an answer.
Say that a user types four for the initial input; the execution configuration looks
like this:
DivideIntoTwelve
main
{ 1> int i = AWAIT RESULT FROM readAnInt;
...
}
readAnInt
{ int num == ?
String s == "four"
DivideIntoTwelve
main
{ 1> int i = AWAIT RESULT FROM readAnInt;
...
}
readAnInt
{ int num == ?
String s == "four"
try{ ... }
catch( ... )
{ ... // dialog anniunces the error
2> num = readAnInt();
}
return num;
}
The restarted method asks the user for a fresh input. If the user types, say, 4, the
704
String s == "four"
try{ ... }
catch( ... )
{ ... // dialog anniunces the error
2> num = AWAIT RESULT FROM readAnInt();
}
return num;
}
readAnInt
{ 3> int num;
...
return num;
}
The restarted copy of readAnInt uses its own local variables to compute the integer,
4, that is returned to the original invocation of readAnInt:
DivideIntoTwelve
main
{ 1> int i = AWAIT RESULT FROM readAnInt;
...
}
readAnInt
{ int num == ?
String s == "four"
try{ ... }
catch( ... )
{ ... // dialog anniunces the error
2> num = AWAIT RESULT FROM readAnInt();
}
return num;
}
readAnInt
{ int num == 4
...
String s == "4"
...
3> return num;
}
The exception handler in the initial invocation of readAnInit finishes its repair by
assigning 4 to num, which is returned to the awaiting main method.
This example shows how an exception handler can repair a truly difficult situation
so that a program can proceed to its conclusion. In this and subsequent chapters, we
11.5. EXCEPTIONS AND EXCEPTION HANDLERS 705
will use recursive invocations to restart methods; Chapters 7 and 12 present other
uses for recursive invocations.
And, if the user typed an incorrect input, the statements would throw an exception
and terminate the program.
It would be better to have a standardized input-view that had skillful methods for
fetching integers, doubles, and strings from the user and helping the user if an input
was incorrectly typed. Once we had such an input view, we could use it as follows:
Better still, we could reuse this same input-view, DialogReader, for all the applications
we write that require input.
Table 7 states the interface we shall use for the input-view class.
As an example, we can change the temperature conversion application seen earlier
in the text to work with the interface in Table 7. This simplifies its main method to
the following:
Next, we write a class that uses JOptionPane and exception handlers to match the
interface. See Figure 8.
Method readString reads a line of text from the dialog, using the standard tech-
nique.
The readInt method uses readString to fetch the user’s input, which must be
converted from a string, s, into an integer in the standard way:
There is a small novelty: The string method, trim(), removes the leading and trail-
ing blanks from the string before it is given to the Integer helper object for con-
version. (Recall from Chapter 3 that string operations are written in the format,
S.op(args), for a string, S, operation, op, and arguments (if any), args.) For example,
if readString(prompt) returned the string, " 123 ", then " 123 ".trim() computes
to "123".
If the user typed a non-integer, then the attempt to convert it into an integer
would throw a runtime exception. Fortunately, the exception handler handles the
exception by restarting the readInt method, just like we saw in the previous section.
Method readDouble works similarly to readInt.
• a description of where the error occurred, namely, the statement number, method,
and class, and a history of all the method invocations that led to the statement.
These pieces of information are packaged into a newly constructed exception object,
which becomes the “result” of the erroneous computation. This result object seeks its
11.6. EXCEPTIONS ARE OBJECTS 707
its handler, which is usually the IDE or JDK that started the program’s execution.
The handler displays the object’s contents on the console window.
There are many classes from which exception objects can be built, e.g., class
ArrayIndexOutOfBoundsException and class NullPointerException are two exam-
ples; both are found in the java.lang package. Figure 9 gives a partial listing of the
exception hierarchy; no doubt, many of its class names are familiar.
Exception objects have several useful methods, presented in Table 10 The methods
extract the information embedded within an exception. We use them momentarily.
As noted in Figure 9, the exceptions we have encountered fall into two groups: run-
time exceptions and input-output exceptions. Runtime exceptions are subtle errors
that are a fact of programming life. Since they can occur at almost every statement,
the Java compiler assumes that every method in a program “throws RuntimeEx-
ception.” In contrast, input-output exceptions are errors that might arise due to a
serious problem with file usage. Such an error is uncommon, and the Java compiler
requires that every method which might generate the error be labelled with throws
IOException.
The usual outcome of an error is the creation of an exception object that finds its
way to its default handler, the IDE or JDK. But in some cases, a programmer might
prefer that the exception object find its way to a different handler, one that does not
terminate execution—the programmer writes an exception handler to “catch” and
“handle” exception objects.
The format of an exception handler goes
try { STATEMENTS }
catch (EXCEPTION_TYPE e)
{ HANDLER }
getMessage(): String Return the message that describes the error that
occurred
toString(): String Return a string representation of the exception ob-
ject; this is usually the class name of the exception
and the message that describes the error.
printStackTrace() Print on the console window the statement number,
method, and class where the error occurred as well
as all the method invocations that led to it.
ex.f();
import java.io.*;
public class ExceptionExample
{ public ExceptionExample() { }
it is better for a programmer to invest her time towards preventing errors with if-
statements rather than recovering from errors with exception handlers.
For example, this statement sequence:
int i = reader.readInt("Please type an array index:");
if ( i > 0 && i < r.length ) // prevent an error
{ System.out.println(r[i]); }
else { System.out.println("invalid index: " + i); }
is always preferred to
int i = reader.readInt("Please type an array index:");
try { System.out.println(r[i]); }
catch (RuntimeException e) // recover from an error
{ System.out.println(e.toString()); }
11.6. EXCEPTIONS ARE OBJECTS 713
Exercises
1. It is usually disastrous for a program to terminate while reading or writing a
file. Here is a program that reads integers from a file and squares them:
import java.io.*;
public class Squares
{ public static void main(String[] args) throws IOException
{ BufferedReader infile = new BufferedReader(new FileReader("ints.dat"));
while ( infile.ready() )
{ int i = new Integer(infile.readLine().trim()).intValue();
System.out.println(i + " squared is " + (i*i));
}
infile.close();
}
}
Revise this class so that an invalid input line causes the program to display
Illegal input---skipping to next line and causes the program to proceed
to the next line of input.
11.7 Summary
We summarize the main points of the chapter.
New Construction
• exception handler:
New Terminology
• token: a “word” within a string or a line of text
• sequential file: a file that is a sequence of symbols. A sequential file can be read
(or written) only from front to back, like a book whose pages can be turned
only forwards. Two forms of sequential file are
• random-access file: a file that can be read or written in any order at all.
• ASCII and Unicoding: two coding formats for representing characters saved
within a file exception: an error that occurs during program execution (e.g.,
executing 12/0 throws a division-by-zero exception). Normally, an exception
halts a program unless an exception handler construction is present to handle
(repair) the exception (see the exception handler in readString above, which
handles an input-failure exception).
Points to Remember
Here are some notions regarding files to keep in mind:
One uses the print and println methods of the object to write text to the file.
An input file is easily created from a BufferedReader object, e.g.,
One uses the readLine method to read one line of text from the file.
• Errors that might arise during file usage are called input-output exceptions, and
a method in which an input-output exception might arise must be labelled with
throws IOException.
Exceptions are in fact objects, and a programmer can write an exception han-
dler, try ... catch, to “catch” and “handle” an exception.
(b) Next, modify PrettyPrint so that (i) hyphenated words and phrases con-
nected by dashes can be broken across lines; (ii) two blanks, rather than
one, are placed after a sentence-ending period.
(c) Finally, modify PrettyPrint so that the output lines are right justified.
2. Write a file merge program: It reads as its input two files of integers (one integer
per line), where the integers in each file are sorted in nondescending order. The
output is a new file, containing the contents of the two input files, of the integers
sorted in nondescending order.
3. Write a program that reads a sequential file and calculates the average length
of its words, its shortest nonnull word and its longest word. (Here, a “word” is
a sequence of non-blank, non-tab, non-newline characters. One text line might
contain multiple words.)
4. Write a program that reads a sequential file of integers and calculates the mean
of the integers, the maximum and minimum integers, and the standard deviation
of the integers, where mean and standard deviation are defined as
where the input file contains the m integers, n 0, n 1, ..., n m. (Recall the
standard deviation tells us that two-thirds of the integers in the file fall in the
range (Mean - StdDev) .. (Mean + StdDev).)
(a) Make the program print the numbers of occurrences of words of lengths 1
to 9 and 10-or-more.
(b) Next, modify the program to display its answers in a bar graph, e.g.,
Frequency of word occurrence:
------------------------------
1: ****
2: *******
3: *********
4: ****
5: ******
6: **
...
718
(c) Next, modify the program to count occurrences of distinct words only, that
is, multiple appearances of the word, “the”, in the file count for only one
occurrence of a three-letter word.
6. Write a program that reads a Java program, removes all /* ... */ and // ...
comments, and leaves the program otherwise unaltered.
(a) The input is a sequential text file, where sequences of n repeating charac-
ters, aaa...a, of length 4 to 9 are compressed into \na For example, the
line
Thisssss is aaaa testtt 22225.
is compressed to
Thi\5s is \4a\6 testtt \425.
8. Write a lexical analyzer for numerical and logical expressions: The program’s
input is an arithmetic expression containing numerals, parentheses, and the
operators +, -, *, /, < , <=, &&, and !. The output is a file of integers, where the
input is translated as follows:
For example, the input ( 2+34 <=(-5+6)) && !(789==0)) translates to the out-
put sequence 1 0 2 3 0 34 8 1 4 0 5 2 0 6 2 2 9 10 1 0 789 6 0 0 2 2.
9. Write a checkbook accountant program. The input is sequential file that be-
gins with the account’s initial balance, followed by a sequence of deposit and
withdrawal transactions. The output is a history of the account, showing each
transaction and the current balance after the transaction.
11.9. BEYOND THE BASICS 719
10. Write a program that reads a file of persons’ names and addresses and a file
that contains a “form letter” (e.g., a letter asking the recipient to subscribe to
a magazine) and creates for its output a collection of output files, one output
file for each person’s name, where each output file contains the form letter
personalized to the person’s name.
The statement, int c = infile.read() reads a character from the input file, but the
character is returned as an integer. (This hides the differences between the one-byte
ASCII codings of characters used by some computers and the two-byte Unicode cod-
ings used by others; both codings are accommodated by using an integer.) The write
method copies the character to the output file.
If one wishes to work with the character that is read, it must be cast into type
char, to inform the Java compiler that it is indeed a character:
int c = infile.read();
char d = (char)c;
String s = "abc" + d;
Conversely, if one tries to write an arbitrary integer, e.g., outfile.write(-9999), the
write method sends an “error character” to the output file.
In a similar way, one can read and write integers and booleans.
As the name suggests, entire objects can be written to an ObjectOutputStream
and read from an ObjectInputStream. Figure 12 displays a program that writes an
integer and several objects to a binary file and then reads the file and restores the
objects.
Objects are written to files with the writeObject method. Of course, objects often
have fields that hold addresses of other objects, as is the case with ComposedCell c in
the Figure. So, writeObject(c) saves not only c’s object but also the objects whose
addresses are held by c’s fields, x and y. An integer and a Cell object are also written,
to show that different types of values can be mixed in the binary file.
When the file test.ob is reopened as an input file, the file’s contents must be read
in the same order in which it was written. This means the ComposedCell object must
be read first:
Object ob = in.readObject();
ComposedCell m = (ComposedCell)ob; // cast ob into a ComposedCell
The readObject method returns a result of type object; this object must be cast into
type ComposedCell, and at this point, the object is restored. The integer and Cell
object are read next.
Both classes ComposedCell and Cell are labelled implements Serializable to as-
sert that objects created from the classes can be saved in sequential binary files. (The
Java compiler verifies that a “serializable” class contains only fields that can be safely
copied onto files.) Also, a method that reads a binary file of objects must state throws
ClassNotFoundException to warn of the possibility that an object of unknown data
type (class) might be read.
Here are some examples of objects for input and output. To assemble an object
that reads strings (lines of characters) from a file, data.in, we might say
BufferedReader infile = new BufferedReader
(new InputStreamReader(new FileInputStream("data.in"));
First, new FileInputStream("data.in") locates data.in and creates an input stream
whose read method reads one byte at a time. Next, new InputStreamReader(...)
creates a character stream whose read method uses FileInputStream’s read to as-
semble one character at a time. Finally, new BufferedReader(...) creates a buffered
reader whose readLine method uses InputStreamReader’s read to assemble one full
line of characters and return the line as a string.
As a convenience, the composed object, new InputStreamReader(new FileInputStream("data.in")),
can be created in one step with new FileReader("data.in"):
BufferedReader infile = new BufferedReader(new FileReader("data.in"));
and this is the pattern used in this chapter to build most input-file objects. Also, the
declaration,
FileReader in = new FileReader("data.in");
creates a reader from which we can read characters one at a time. (But the reader is
not buffered.)
In a similar way,
PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));
creates a buffered writer that uses a writer, new FileWriter("file"). The latter ab-
breviates the character output stream, new OutputStreamReader(new FileOutputStream("test.txt")).
We create a file to which objects are written with this declaration:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("d.ob"));
This builds an object stream whose writeObject method uses FileOutputStream’s
write to write objects as sequences of bytes to the file, d.ob.
The tables that follow provide details for the classes in Figure 13.
724
The file chooser lets a user click on a folder to open it and click on a file name to
select a file. When the user presses the Select button, the path name of the selected
file is remembered, and the program that created the file chooser can retrieve the
path name.
A simple way to create and show a file chooser is
JFileChooser chooser = new JFileChooser();
chooser.setDialogTitle("Choose a file");
int result = chooser.showDialog(null, "Select");
The resulting dialog displays the contents of the folder (directory) of the user’s “home”
folder; the dialog’s title is set by setDialogTitle. After the user selects a file and
presses the button labelled Select, the selected file’s path name is retrieved and used
as follows:
if ( result == JFileChooser.APPROVE_OPTION )
11.9. BEYOND THE BASICS 731
class JFileChooser
Constructors
JFileChooser() Constructs a file chooser which points to the con-
tents of the user’s home folder (directory)
JFileChooser(String Constructs a file chooser which points to the con-
path name) tents of the folder path name.
JFileChooser(File path name) Constructs a file chooser which points to the con-
tents of the folder path name.
Methods
getSelectedFile(): File Return the file selected in the file chooser’s view
setDialogTitle(String title) Set the title of the file chooser’s dialog to title
{ File f = chooser.getSelectedFile();
String path_name = f.getPath();
... // use path_name to create a file object
}
else { System.out.println
("Error: Select button was not pushed or pushed with no file selected");
}
The file chooser returns an integer code which equals JFileChooser.APPROVE OPTION if
the user selected a file (or typed a file name into the dialog’s text area) and terminated
the dialog by pushing the Select button (or pressing Enter in the text area). The
getSelectedFile method returns a File object from which one extracts the path
name with getPath.
Figure 14 shows some of the methods for class JFileChooser.
Exercise
Extend the text editor in Figure 33, Chapter 10 so that it has a menu item that reads
a file into the editor’s text area and one that saves the contents of the text area into
a file.
Appendix I
Grammatical Notation
The syntax of a computer language are defined by a set of grammar rules or “equa-
tions” that define the phrases’ precise appearance. An example best introduces this
format.
Using an English description, we might define a number to appear as a whole-
number part followed by an optional fraction part. Both whole-number and fraction
parts consist of nonempty sequences of numerals; the fraction part is preceded by a
decimal point. These grammar rules formalize the description:
The words in upper-case letters name the phrase forms. Thus, the first rule tells
us that a NUMBER phrase consists of a WHOLE PART followed by a FRACTION PART; the
question mark states that the FRACTION PART is optional. The second and third rules
have obvious readings; note the decimal point in the third rule.
The asterisk in the fourth rule stands for “zero or more”—a NUMERAL SEQUENCE
consists of one NUMERAL followed by zero or more additional NUMERALs. The vertical
bars in the last rule are read as “or.” Unless stated otherwise, appearances of ?, *,
and | in grammar rules will stand for the notions just described.
For example, 123.4 is a NUMBER because 123, the WHOLE PART, is a NUMERAL SEQUENCE
and because .4 is a FRACTION PART, where 4 is a NUMERAL SEQUENCE.
For conciseness, the first three grammar rules can be compressed into just one:
The double-bracket pairs, [[ and ]], enclose a phrase; because of the question mark
suffixed to it, the entire phrase is optional. We also use double brackets to enclose
phrases that can be repeated zero or more times, e.g.,
NUMBER_LIST ::= NUMBER [[ , NUMBER ]]*
This rule defines the syntax of a list of numbers separated by commas, e.g., 1, 2.34,
56.7 is a NUMBER LIST as is merely 89.
Double brackets can be used to enclose alternatives, e.g., this grammar rule defines
exactly the two words, “cat” and “cut”:
WORDS ::= c [[ a | u ]] t
As the examples show, spaces within the grammar rules do not imply that spaces
are required within the phrases defined by the rules. For example, we normally write
a NUMBER, like 56.7, with no internal spaces. We follow this convention in the sections
that follow.
Java Definition
We now define the Java subset used in this text. For exposition, we present the
language one construct at a time. Each construct is defined by
• one or more grammar rules, which define syntax;
• an explanation of the grammar rules and restrictions (e.g., data-typing), if any
on the phrases they define;
• one or more examples of Java phrases that are defined by the syntax;
• a summary of the construction’s semantics.
Program Unit
PROGRAM_UNIT ::= [[ package IDENTIFIER ; ]]?
IMPORT*
[[ CLASS | INTERFACE ]]
IMPORT ::= import PACKAGE_NAME ;
PACKAGE_NAME ::= IDENTIFIER [[ . IDENTIFIER ]]* [[ . * ]]? ;
A Java program unit begins with an optional package affiliation, followed by zero or
more import statements, followed by either a class definition or an interface definition.
(Important: the asterisk in the phrase, [[ . * ]], is indeed an asterisk to be included
in the package name; it is not indicating a repetition.) An IDENTIFIER is a name, as
defined in “What is an Identifier?” in Chapter 3. CLASS and INTERFACE are defined
below.
Example:
726
package MyGraphicsFrames;
import java.util.GregorianCalendar;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public abstract class MyFrame extends JFrame
{ ... }
Semantics: A package affiliation groups a Java unit with others in the same-named
package (see “Packages” in Chapter 9). The import statements make available the
classes contained in the imported packages (see Figure 4 and “Java Packages” in
Chapter 2). When the . * suffix is omitted, then the PACKAGE NAME must name a
specific class in a specific package, and it is only this one class that is available for
use.
Class
CLASS ::= public [[ abstract | final ]]?
class IDENTIFIER EXTEND? IMPLEMENT*
{ FIELD*
CONSTRUCTOR*
METHOD*
}
EXTEND ::= extends IDENTIFIER
IMPLEMENT ::= implements IDENTIFIER_LIST
IDENTIFIER_LIST ::= IDENTIFIER [[ , IDENTIFIER ]]*
As indicated by the grammar rule, the keywords, abstract and final are exclusive
and optional. The identifier mentioned in EXTEND must be the name of a class; the
identifiers in IMPLEMENT must be names of interfaces. FIELD, CONSTRUCTOR, and METHOD
are defined in subsequent sections.
Example:
Interface
INTERFACE ::= public interface IDENTIFIER EXTEND?
{ [[ public abstract? RETURN_TYPE METHOD_HEADER ; ]]* }
An interface contains zero or more header lines of methods. RETURN TYPE and METHOD HEADER
are defined below in the sections, “Type” and “Constructor,” respectively.
Example:
public interface MyInterface
{ public void f();
public int h(GregorianCalendar g);
}
Semantics: An interface lists method names that a class might possess; a class
that does so is said to implement the interface. See “Interfaces” in Chapter 9 for
details and examples.
Field
FIELD ::= VISIBILITY static? final? DECLARATION
VISIBILITY ::= public | private | protected | (nothing)
• public: the component can be referenced by all components of all classes in the
program
• private: the component can be referenced by only those components that are
defined in the same class as this component
Public and private visibilities are used extensively in examples throughout Chapter
5. Fields are almost always labelled private. Protected visibility is used occasionally
with subclasses—see “Subclasses and Method Overriding” and Figure 15 in Chapter
9 for an example. The last visibility, called package visibility, is not used in this text.
Constructor
CONSTRUCTOR ::= public METHOD_HEADER
{ [[ super ( ARGUMENT_LIST ) ; ]]?
STATEMENT*
}
METHOD_HEADER ::= IDENTIFIER ( FORMALPARAM_LIST? )
FORMALPARAM_LIST ::= FORMALPARAM [[ , FORMALPARAM ]]*
FORMALPARAM ::= TYPE IDENTIFIER
A constructor method’s name must be the same as the class in which the constructor
is included. A class can have more than one constructor method, provided that
each constructor is uniquely identified by the quantity and data types of its formal
parameters. (Such a situation is called overloading the constructor.) The formal
parameters in the METHOD HEADER must be distinctly named.
If the class in which the constructor appears extends another class, then the first
statement of the constructor can be an invocation of the constructor in the super
class—the invocation is called super. STATEMENT is defined in a later section.
ARGUMENT LIST is defined in the section, “Invocation.” TYPE is defined below.
Examples:
public MyFrame()
{ setSize(200, 100);
setVisible(true);
}
{ setTitle(title);
setSize(width, width/2);
setVisible(true);
}
Type
TYPE ::= PRIMITIVE_TYPE | REFERENCE_TYPE
PRIMITIVE_TYPE ::= boolean | byte | int | long | char | float | double
REFERENCE_TYPE ::= IDENTIFIER | TYPE[]
RETURN_TYPE ::= TYPE | void
Note that type names like String, GregorianCalendar, etc., are in fact identifiers.
Examples:
double[][]
MyFrame
MyInterface
Method
METHOD ::= VISIBILITY [[ abstract | static ]]? final? RETURN_TYPE
METHOD_HEADER METHOD_BODY
METHOD_BODY ::= { STATEMENT* } | ;
730
The method is labelled abstract if and only if the method’s body consists of just a
semicolon. A static method is invoked by other static methods, e.g., main. A final
method cannot be overridden (see below). A method’s formal parameters must have
distinct names.
Examples:
private double sum(int i, double j)
{ return i + j; }
Statement
STATMENT ::= DECLARATION
| RETURN
| IF | SWITCH
| WHILE | DOWHILE | FOR
| TRY_CATCH
| THROW
| STATEMENT_EXPRESSION ;
| { STATEMENT* }
| ;
STATEMENT_EXPRESSION ::= OBJECT_CONSTRUCTION | INVOCATION | ASSIGNMENT
The various statement forms are defined in the sections that immediately follow.
Examples and Semantics: Statements are executed in sequence, from “left to
right.” Statement expressions are statements that return results; a statement expres-
sion can be used where an expression is expected. Examples are
731
Declaration
DECLARATION ::= TYPE IDENTIFIER [[ = INITIAL_EXPRESSION ]]? ;
int i = 2 + 3;
GregorianCalendar[] days_in_this_month;
Return
RETURN ::= return EXPRESSION ;
The statement can appear only in the body of a method that has a non-void return
type stated in its header line; the data type of EXPRESSION must be a subtype of the
return type. (See Section “Type” for the definition of RETURN TYPE.)
Examples:
Conditional
IF ::= if ( EXPRESSION ) { STATEMENT* } [[ else { STATEMENT* } ]]?
SWITCH ::= switch ( EXPRESSION )
{ [[ case EXPRESSION : { STATEMENT* break ; } ]]*
default : { STATEMENT* }
}
732
The expression part of the if-statement must have data type boolean. The first
expression of the switch-statement must have data type integer, char, or byte.
The second expression of the switch-statement must be an integer-, char-, or
byte-typed constant expression, that is, an integer expression that is constructed
from numeric constants, arithmetic operations, and fields that are static and final.
Here is an example that uses a convoluted numeric constant:
The Java compiler calculates the integer represented by the constant expression and
replaces the expression by the integer. (Here, (int)(k / 3.3) + 1 is replaced by 1.)
In practice, constant expressions are merely integer and character constants, e.g., 5
and ’a’.
Additional Example:
if ( i > 0 && i != 2 )
{ j = sum(i, 2.2); }
else { if ( b )
{ j = 0; }
}
Iteration
WHILE ::= while ( EXPRESSION ) { STATEMENT* }
DOWHILE ::= do { STATEMENT* } while ( EXPRESSION ) ;
FOR ::= for ( [[ DECLARATION | ASSIGNMENT ; ]]
EXPRESSION ;
STATEMENT_EXPRESSION )
{ STATEMENT* }
733
The test expression for each of the three loops must be typed boolean. Although
the for-loop accepts a STATEMENT EXPRESSION as the third part of its header line, in
practice this is an assignment statement. Alas, there is no restriction regarding the
variables declared, referenced, and assigned in the three parts of the for-loop’s header
line.
Examples:
for ( int i = 0; i != r.length; i = i + 1 )
{ while ( r[i] > 0 )
{ r[i] = r[i] - 2; }
}
Semantics: As long as the test expression evaluates to true, the body of a loop
repeats. Chapter 7 displays numerous examples. The do-while-loop is a minor vari-
ation of the while-loop—the body executes once before the loop’s test is computed;
see “The do-while loop” in Chapter 7.
The for-loop executes its declaration-assignment on entry; next, the test expression
is computed, and as long as the text computes to true, the loop’s body is executed,
followed by the statement-expression in the loop’s header line. For loops are intro-
duced in “The for-loop” in Chapter 7 and are extensively used with arrays in Chapter
8.
Exception Handler
TRY_CATCH ::= try { STATEMENT* } CATCH_LIST
CATCH_LIST ::= CATCH CATCH*
CATCH ::= catch ( TYPE IDENTIFIER ) { STATEMENT* }
THROW ::= throw EXPRESSION ;
The data type listed in the CATCH clause of an exception handler and the data type
of the EXPRESSION in the THROW statement must be a subtype of Throwable.
Example:
try { if ( i != 0 )
{ j = j / i; }
else { throw new RuntimeException("i is zero"); }
}
catch (RuntimeException e)
{ System.out.println(e.toString());
j = 0;
}
Semantics: The statements in the try clause of an exception handler are executed
as usual, unless a throw statement is executed. The throw aborts usual execution
and forces an immediate search of the clauses in the CATCH LIST for the first catch
734
clause whose data type is a subtype of the type of the exception object listed in
the throw statement. If a matching catch clause is found, the exception binds to
its formal parameter, the clause’s statements are executed. If no matching catch
clause is found, search for a matching clause proceeds at the next enclosing exception
handler, as found by search the prior sequence of statements that were executed.
See “Exceptions Are Objects” in Chapter 11 for details.
Object Construction
OBJECT_CONSTRUCTION ::= new IDENTIFIER ( ARGUMENT_LIST? )
| new ARRAY_ELEMENT_TYPE DIMENSIONS
ARGUMENT_LIST ::= EXPRESSION [[ , EXPRESSION ]]*
ARRAY_ELEMENT_TYPE ::= PRIMITIVE_TYPE | IDENTIFIER
DIMENSIONS ::= [ EXPRESSION ] [[ [ EXPRESSION ] ]]* [[ [] ]]*
Objects are constructed in two forms: individual objects and array objects. For
constructing the former, the identifier must be a class name, and its argument list
must contain the same quantity of parameters as (one of) the constructor methods
included in the class; the data type of each argument must be a subtype of the data
type listed with the corresponding formal parameter in the constructor method.
Examples:
new GregorianCalendar()
new MyFrame(300, "")
When array objects are constructed, the data type of the array’s individual el-
ements is listed first, followed by the dimensions. The individual elements can be
of primitive type or a type defined by a class name. The dimensions are indicated
by pairs of brackets, where the size of each dimension is indicated by an expression
embedded within a bracket pair. The quantity of at least the first dimension must be
indicated.
Examples:
new int[4]
new GregorianCalendar[12][]
new double[size][size * size][][]
Semantics: The statement creates an object from class IDENTIFIER (or an array
that holds values of the array-element type). Examples pervade the text—see “An
Application that Creates an Object” in Chapter 2 for a simple one, see “Revised
Syntax and Semantics of Classes” in Chapter 5 for details about object creation, and
see “Formal Description of Arrays” at the end of Chapter 8 for examples of array
object construction. The class’s constructor method whose formal parameters best
match the argument list is executed; see “Formal Description of Methods” in Chapter
5 for details about the matching process.
735
Method Invocation
INVOCATION ::= [[ RECEIVER . ]]? IDENTIFIER ( ARGUMENT_LIST? )
RECEIVER ::= this | super
| IDENTIFIER
| RECEIVER [ EXPRESSION ]
| RECEIVER . IDENTIFIER
| STATEMENT_EXPRESSION
| ( RECEIVER )
The identifier mentioned in the INVOCATION rule is a method name; the identifier in
RECEIVER can be either a variable name whose data type is a reference type (that is,
the variable holds the address of an object) or it can be the name of a class, which
occurs when a static method is invoked; see below.
The argument list must contain the same quantity of parameters as the number
of formal parameters listed in the header line of the invoked method, IDENTIFIER.
The data type of each argument must be a subtype of the data type listed with the
corresponding formal parameter.
Examples and Semantics: To send a message (that is, to invoke a method), the
(address of the) object where the method lives must be determined—the object is
the “receiver” of the message. If the RECEIVER is omitted from the invocation, then
the receiver is the same object where the invocation is situated. Private methods are
usually invoked this way:
public class C
{ private static double sum(int i, double j)
{ return i + j; }
...
}
736
All invocations in the example ultimately invoke setTitle in the superclass, JFrame;
see “Customizing Frames with Inheritance” in Chapter 4. The semantics of this is
the same as an omitted RECEIVER.
super asserts that the receiver should be the same object as the invocation’s but
the invoked method must be selected from the superclass from which the object is
built:
import javax.swing.*;
public class MyFrame extends JFrame
{ public MyFrame()
{ super.setTitle("MyFrame"); }
public setTitle(String s)
{ }
}
invokes the setTitle method located in class JFrame and ignores the one in MyFrame.
See Figure 16, Chapter 9, for a significant example.
When an identifier defines the receiver, the identifier is usually a variable that
holds the address of an object, as g does here:
But the identifier can also be a class name when the invoked method is static. Here
is an example:
public class C
{ public static int one() { return 1; } }
public class D
{ public static void main(String[] args)
{ System.out.println( 2 * C.one() ); }
}
Table 9 of Chapter 3 lists a variety of static methods that are invoked with the
receiver, Math, e.g., Math.sqrt(2).
A receiver can be indexed if it names an array object, e.g.,
Similarly, an object that contains a public field that holds an object can be indexed
by an identifier:
737
public class C
{ public GregorianCalendar g = new GregorianCalendar();
...
}
public class D
{ ...
C x = new C();
... x.g.getTime() ...
}
Finally, a statement expression can define a receiver. This happens when an object
is constructed anew, e.g., new GregorianCalendar().getTime(), and when a method
invocation returns an address of an object as its result:
public class C
{ public GregorianCalendar f() { return new GregorianCalendar(); }
...
}
public class D
{ ... (new C().f()).getTime() ... }
Here, new C().f() is the invocation that defines the receiver for the invocation of
getTime.
Once the receiver is established, the method within the receiver is selected. (Note
the impact of super on this selection.) Next, the arguments are evaluated, bound
to the formal parameters of the selected method, and the selected method executes.
Precise descriptions of these steps are found in “Formal Description of Methods” in
Chapter 5, “Semantics of Overloading” in Chapter 9, and “Semantics of Overriding”
also in Chapter 9.
Examples of method invocation abound in the text; Chapter 5 is devoted to this
one topic.
Assignment
ASSIGNMENT := VARIABLE = EXPRESSION
VARIABLE ::= IDENTIFIER
| RECEIVER [ EXPRESSION ]
| RECEIVER . IDENTIFIER
The data type of the assignment’s expression must be a subtype of the variable’s data
type.
Examples and Semantics: An assignment places the value of its right-hand side
expression into the cell named by the left-hand side variable. Variables are usually
identifiers, e.g., x in int x; x = 0.
738
public class C
{ public int x; }
public class D
{ ... C c = new C();
c.x = 1; ...
}
Expression
EXPRESSION ::= LITERAL
| VARIABLE
| EXPRESSION INFIX_OPERATOR EXPRESSION
| PREFIX_OPERATOR EXPRESSION
| ( EXPRESSION )
| ( TYPE ) EXPRESSION
| EXPRESSION instanceof REFERENCE_TYPE
| this | null
| STATEMENT_EXPRESSION
LITERAL ::= BOOLEAN_LITERAL
| INTEGER_LITERAL | LONG_LITERAL
| FLOAT_LITERAL | DOUBLE_LITERAL
| CHAR_LITERAL | STRING_LITERAL
INFIX_OPERATOR ::= + | - | * | / | %
| < | > | <= | >= | == | !=
| || | &&
PREFIX_OPERATOR ::= - | !
INITIAL_EXPRESSION ::= EXPRESSION
| { [[ INITIAL_EXPRESSION_LIST ]]? }
INITIAL_EXPRESSION_LIST ::= INITIAL_EXPRESSION [[ , INITIAL_EXPRESSION ]]*
As always, infix and prefix operators require arguments whose data types are ac-
ceptable to the operators, e.g., the multiplication operator, *, requires arguments of
numeric data type. Literals are constants, e.g., 3 and true; see Table 2 of Chapter 3.
INITIAL EXPRESSIONs are used to initialize variable declarations; the set expres-
sions are used to initialize array variables, e.g., int[][] r = { {0,1}, {1,3,5} {},
739
{1}}, constructs and initializes an array with 4 rows where each row has a different
number of columns.
Semantics and Examples: As described in Chapter 3, expressions are computed
from left to right, producing a result that can be a primitive value or the address of
an object. Consider each of the clauses of the syntax definition for EXPRESSION:
• A variable, e.g., r, computes to the value held in the variable’s cell. This value
can be a primitive value or (the address of) an object.
• An infix expression, e.g., (-2 > 3 + (4 * 5)) || (r[i] == sum(j, j)) com-
putes its operands from left to right, and operates on the two results, producing
an answer; a prefix expression computes its operand’s value and performs its
operation.
computes to true, because the data type within the object is MyFrame even as
the Java compiler uses type JFrame for the values held in cell f.
• this computes to (the address of) the very object in which the expression is
embedded; null is the “no value” value.
• Statement expressions are computed just like statements, but they return values
that are their “results”:
740
– The value of an OBJECT CONSTRUCTION is (the address of) the newly con-
structed object.
– The value of an INVOCATION is the result returned by the invoked method.
– The value of an ASSIGNMENT is the value of the assignment’s right-hand side
expression, e.g., the value of i = 2 * 3 is 6.
Appendix II
Every class and interface definition defines a reference data type, e.g., public class
MyFrame extends JFrame defines type MyFrame, and public interface UpdateableView
defines type UpdateableView.
The Java compiler calculates data types to enforce compatibility in expression
calculation, assignment, parameter binding, and results returned from methods. For
example, given
int i = 200;
JFrame f = new JFrame();
f.setSize(i + 1, i / 2);
the compiler calculates data types of the numerals, variables, and expressions to
validate in advance of these statements’ execution that they will behave correctly:
• The initialization of int variable, i, will execute correctly because 200 has type
int.
• When a new JFrame() object is constructed, its address can be saved in f’s cell,
because the cell is prepared to hold addresses of JFrames.
• Whatever value is held in f’s cell will be the address of a JFrame object and
will possess a method named setSize, therefore the setSize message can be
correctly sent to the object named by f.
Because of the compiler’s work, none of the above checks must be repeated when the
program executes.
Subtypes
The Java compiler allows some flexibility in its data type calculation—given a context
where a value of data type, T1, is required, the Java compiler will allow a value whose
data type, T2, is a subtype of T1. We write T2 <= T1. One simple example is
742
int i = 3;
double d = i * 2.5;
Within the context, f( ), a double is required, but 3 suffices because its data type
is int and int <= double.
Subtypes prove crucial to object usage, because data types are embedded into
objects when objects are constructed during program execution. Consider
Component f = new JFrame();
...
if ( f instanceof JFrame )
{ ((JFrame)f).setVisible(true); }
the JFrame object constructed in the first statement has an internal data type of
JFrame, even as the Java compiler has calculated that f holds values of type Component.
The assignment is approved by the compiler because JFrame <= Component; see Chap-
ter 10.
When the Java compiler calculates data types, it does not execute the statements
themselves, and when the compiler examines the above if-statement, it knows merely
that f has data type Component; it cannot answer whether the object held by f is
truly a JFrame.
Later, when the program is started and the if-statement executes, the instanceof
operation examines the data type embedded within the object named by f to calculate
the answer to the test. If the answer is true, the cast operation lets f be used as a
JFrame-typed object.
where T1 <= T2 if T1 lies no further to the right in the ordering than does T2. For
example, byte <= int, byte <= long, and int <= int.
743
• T1 is an array type, T11[], T2 is an array type, T21[], and we can use these
rules to prove that T11 <= T21
• There is another type, T3, and we can use these rules to prove that T1 <= T3
and T3 <= T2
• T2 is Object
For example, from these definitions,
public interface I1 { ... }
Yet a second way of concluding the same fact is by noting that class D implements
I2, interface I2 extends I1, hence, D <= I1 and then D[] <= I1[].
744
Appendix III
Classes
In its simplest representation, a class is drawn as a rectangle containing the class’s
name.
BankAccount
A method can be specified with just its name, e.g., deposit, or it can be listed with its
formal parameters and its result type, e.g., withdraw(int amount): boolean. Note
that the result type is listed at the right of the method’s specification, preceded by
a colon. Usually, constructor methods are not listed with the class, but they can
appear if the constructor or its parameters are important to understanding the class.
A class’s attributes can be listed as well:
BankAccount
private int balance
deposit(int amount): boolean
withdraw(int amount): boolean
When an attribute is specified, one can assume that the class will have an accessor
method for the attribute, e.g., public int balanceOf() { return balance; }. See
Figure 12, Chapter 6, for this example.
Objects can be that already exist prior to the execution of a program can be
included in a class diagram as well:
CelsiusToFahrenheit2
main System.out
745
An abstract class is specified by writing in italics the class’s name and those
methods that are abstract. (If you write the class diagram by hand, write the word,
“abstract” in front of those words that should be in italics.)
CardPlayer
wantsACard(): boolean
receiveCard(Card c)
showCards(): Card[]
Such an arrangement might appear at an early stage of design, before specifics about
methods and dependencies are decided.
An arrow from one component to another means that the first component depends
on the second. By “depends,” we mean some or all of the following:
• the first component owns or holds the address (a “handle”) to the second in a
private field variable
BounceController AnimationWriter
runAnimation() paintComponent(Graphics g)
BallWriter BoxWriter
paint(Graphics g) paint(Graphics g)
MovingBall Box
(see Table 6) (see Table 5)
relationship:
ClockWriter2 JPanel Graphics
paint setColor
drawString
GregorianCalendar
get
The large, black arrowheads in the diagram assert that every JPanel object will
have its own unique Graphics object, and every ClockWriter2 will have its own
GregorianCalendar.
When one class extends another by means of inheritance, an arrow with a head is
drawn from the subclass to the superclass:
ClockWriter2 JPanel
paint
The arrow indicates that an object created from the subclass will contain the structure
inherited from the superclass; in particular, the subclass’s methods include methods
listed in the superclass. Here, ClockWriter2 is a JPanel extended by additional meth-
ods.
When a component depends on (refers to) an interface, a dotted arrow is drawn
from the component to the interface:
MortgageCalculator BankAccountSpecification
makeMortagagePayment() deposit(int amount)
withdraw(int amount): boolean
Annotations
The usual dependency between components is one-to-one, that is, if an arrow is drawn
from component A to component B, it suggests that every one A object depends on
one B object. To make explicit other quantities of dependency, we annotate the arrow
with integers. For example, Figure 17 of Chapter 6 displayed the class diagram of an
application whose controller manage two bank accounts and their output views:
AccountController2 1
processTransactions() 2 BankAccount
2
BankWriter
The annotations on the arrow state that each one AccountController object depends
upon (uses) two distinct BankAccount objects.
If there is a one-to-many dependency, but the exact quantity of the “many” is
determined only when the application is built, an asterisk can be used in place of
the integer. For example, the slide puzzle application in Chapter 8 is designed to
construct slide puzzles with a varying quantity of movable puzzle pieces. Therefore,
the puzzle board uses at least one and possibly many puzzle pieces:
SlidePuzzleBoard PuzzlePiece
private PuzzlePiece[][] board 1 1..* private int face value
move(int w): boolean
Class Hierarchies
A package of classes that are related by inheritance is depicted in a form of class dia-
gram that uses indentation in place of large arrow heads. For example, if class Window
extends Container, JFrame extends Window, and JApplet extends Container, we might
display the classes in this hierarchy:
Container
|
+-Window
| |
| +-JFrame
|
+-Japplet
Figure 5, Chapter 10, and the API Web pages for the Java packages display such
class hierarchies.
Index
Because the base form of the textbook is an HTML document, the is compiled as
a mapping from terms to section numbers.
Another way to locate a topic is to use your web browser to consult the on-line,
HTML version of the text and use the browser’s “Edit–find in page” search facility.
The HTML source can be found through the link, http://www.cis.ksu.edu/santos/schmidt/ppj.
\noindent
+: 3.1, 3.2, 3.3, 3.5
*, -, /: 3.1, 3.3
<, >, <=, >=: 3.4
!: 6.3, 6.12.1
||: 6.3, 6.12.1
&&: 6.3, 6.12.1
=: 3.2, 3.2.1
==: 3.4, 6.3
!=: 3.4, 6.3
\b (backspace), \t (tab), \n (newline), \r (return), \", \’, \\: 3.5
/** ... */ (multi-line comment): 2.3, 5.9.2
// ... (internal comment): 2.4
\noindent
@author: 5.9.2
@param: 5.3, 5.9.2
@return: 5.5, 5.9.2
@version: 5.9.2
\noindent
abs (of Math): 3.3, 3.13.2
AbsTempFrame view: 10.9
abstract: 9.5
AbstractButton: 10.3, 10.15.6
AbstractTableModel: 10.15.2
abstract class: 9.5, 9.5.1, 9.5.2, 9.6
abstract window toolkit (AWT): 9.5.2, 10.3
AccountController controller: 6.8.8
AccountController2 controller: 6.8.8
AccountWithInterest model: 9.12.1
action event: 10.2, 10.5
action listener: 10.2, 10.5
ActionEvent: 10.3, 10.5, 10.15.6
ActionListener interface: 10.3, 10.5
757
database: 8.6
Database model: 8.6, 9.2.1
debugger: 6.9.3
DecimalFormat: 3.8.1
declaration: 3.2, 3.13.3
definite iteration: 7.2
delimiter: 11.1.1
delItem (of List) 12.6
deselect (of List) 12.6
design, object-oriented: 1.7.2, 7.7, 7.7.1
dialog (graphical): 4.1, 4.1.1, 6.4, 10.3, 10.10
DialogReader input view: 11.5.2
directory: 1.1
display: 1.4, 2.1
dispose (of Window): 10.3, 10.15.6
divergence: 7.2, 7.4
divide-and-conquer algorithm: 8.14.4
doClick (of AbstractButton): 10.3, 10.15.6
double: 3.3
Double: 3.8.1, 9.7
doubleValue (of Double): 3.8.1
do-while loop: 7.25.1
drawArc (of Graphics): 4.3
DrawArt application: 5.2.1
drawLine (of Graphics): 4.3
drawOval (of Graphics): 4.3
drawRect (of Graphics): 4.2.2, 4.3
drawString (of Graphics): 4.3
dummy class: 6.8.8, 6.9.3
\noindent
E (of Math): 3.13.2
EAST (of BorderLayout): 10.3, 10.6
EditFrame view: 10.11.1
EditModel model: 10.11.1
EggFieldWriter output view: 7.22
elements of array: 8.1, 8.4, 8.9
else: 6.2, 6.2.1
EOFException: 11.6
equals (of String): 3.5
error, execution: see exception
errors, grammar (syntax): 2.6
exectute a program: 2.2
762
\noindent
quick sort: 8.14.4
\noindent
ragged array: 8.9
random (of Math): 3.13.2
random-access file: see file, random access
read (of BufferedReader): 11.9.3
read (of FileInputStream): 11.9.3
read (of InputStreamReader): 11.9.3
read (of FileReader): 11.9.1
read (of ObjectInputStream): 11.9.3
read a file: 11.2
readBoolean (of ObjectInputStream): 11.9.3
readDouble (of ObjectInputStream): 11.9.2, 11.9.3
Reader: 11.9.3
readInt (of ObjectInputStream): 11.9.2, 11.9.3
readLine (of BufferedReader): 11.2.2, 11.9.3
readObject (of ObjectInputStream): 11.9.2, 11.9.3
ready (of BufferedReader): 11.2.1, 11.9.3
receiver of a message: 5.1, 5.9.5
Record interface: 9.2.1
recursion: 6.8.8, 7.20, 11.5.2
recursion, counting down: 7.20.1
recursion and loops: 7.11.1
recursion, multiple: 7.11.2
recursion, tail: 6.8.8
recursive definition: 7.20
RecursivePictureWriter output view: 7.22
reference (a variable): 3.2, 3.13.3
reference data type: 3.5, 3.6, 3.13.3
relational operation: 6.3
removeTableModelListener (of TableModel): 10.15.2
repaint (of JFrame and Component): 4.8.1, 10.3, 10.15.6
repaint (of JPanel): 5.4, 10.3, 10.15.6
ReplaceFrame view: 10.11.1
replaceItem (of List) 12.6
replaceRange (of JTextArea): 10.3, 10.11, 10.15.6
replaceSelection (of JTextComponent): 10.3, 10.15.6
representation invariant: 4.4
responsibility: 1.7.2, 5.2, 5.4, 6.7, 7.7
result from a method (see also, function): 5.5
return: 5.5, 6.5.3
771
setVisible (of JFrame and Component): 4.2.1, 4.8.1, 10.3, 10.4, 10.15.6
showConfirmDialog (of JOptionPane): 10.3, 10.10, 10.15.6
showDialog (of JFileChooser): 11.9.4
showInputDialog (of JOptionPane): 4.1, 6.4, 10.3, 10.10, 10.15.6
showMessageDialog (of JOptionPane): 4.1.1, 10.3, 10.10, 10.15.6
sin (of Math): 3.13.2, 5.9.3
signature of a method (see header line of a method)
simulation, computation as: 1.7.2, 7.7, 7.9, 10.15.4
sleep (of Thread): 2.2.1, 7.9
SlidePuzzleBoard model: 8.10
software architecture: 1.5 see also: architecture, class diagram
sorting an array: 8.14.1
SOUTH (of BorderLayout): 10.3, 10.6
specification: 5.4, 5.9.2, 6.8.3, 6.8.5, 6.12.2, 7.7.1, 7.9, 8.6, 8.7, 9.1, 9.4, 10.4
specification of a method: 5.2.1, 5.4
spreadsheet: 10.15.2
sqrt (of Math): 3.13.2, 5.9.3
StackedEggsWriter output view: 5.6
StaffDatabase model class:
statement: 2.3
static: 5.9.3
static public field: 5.9.3
static public final field: 8.7
static method: 5.9.3
stepwise refinement: 1.7.1
storage, primary: 1.1
storage, secondary: 1.1
stream: 11.9.3, see also: file
string tokenizer: 11.1.1, 11.3.1
string: 2.3, 3.5, 11.1
String: 3.5, 11.1
String[] args: 3.8, 8.4
StringBuffer: 11.1
StringIndexOutOfBoundsException: 11.6
StringTokenizer: 11.1.1, 11.3.1
stub: 6.8.8, 6.9.3
subclass: 4.2.2, 9.3, 9.6
substring (of String): 3.6, 6.8.7
subtype: 4.8.1, 9.4, 9.6, 9.9.1, 9.9.2
super: 9.3, 9.9.1, 9.12.1, 9.12.2, 10.5
superclass: 4.2.2, 9.3, 9.6
Swing: 10.3
773
switch: 6.6
syntax: 2.9.1
System.in: 11.3
System.exit(0): 6.5.2, 10.5.3
System.out: 2.1
\noindent
TableModel interface: 10.15.2
tan (of Math): 3.13.2
TempFrame view: 10.9
TempFrame2 view: 10.10
termination expression for loop: 7.25.3
termination of loop, proving : 7.25.3
terminate an application: 4.2.1, 7.4
testing an applet: 4.8.4
testing arrays: 8.11
testing attributes: 6.9.2
testing, black-box: 4.5
testing classes: 6.7, 6.9, 6.9.3
testing loops: 7.8
testing methods: 6.2.1, 6.7, 6.8.4, 6.9
testing, white-box (clear-box): 4.5
testing programs that receive input: 4.5
text component (graphical): 10.3, 10.9, 10.11
this object: 4.3.1, 5.4, 10.5
thread (of execution): 10.15.4
ThrobbingBall model: 10.6.1
ThrobController controller: 10.6.1
ThrobPanel view : 10.6.1
ThrobFrame view: 10.6.1
Throwable: 11.6
throw: 6.5.1, 11.6.1
throws IOException: 11.2.1, 11.2.2, 11.6
time complexity of an algorithm: 8.14.4
time complexity, linear: 8.14.4
time complexity, logarithmic: 8.14.4
time complexity, quadratic: 8.14.4
top-down design: 1.7.1
toLowerCase (of String): 3.6, 6.8.7
toString (of Object: 9.7, 10.8
toString (of Throwable: 11.6
toUpperCase (of String): 3.6, 6.8.7
translate (of Point): 10.3, 10.15.6
774