Anda di halaman 1dari 844

Programming Principles

in Java:
Architectures and Interfaces

David Schmidt
Computing and Information Science Department
Kansas State University

January 2003 Edition


This text is Copyright 2003 by David Schmidt. Reproduction is allowed by permission
of the author, who can be contacted at schmidt@cis.ksu.edu
Preface
My nine-year-old niece is a computer programmer: Using her father’s PC and
Disney’s “Print Studio” software, she constructs programs that print greeting cards
for her friends. My niece has no training in art, graphic design, or computer pro-
gramming, yet she programs greeting cards to her satisfaction—she practices “naive
programming.”
In similar and ever increasing fashion, naive programmers use visual, “drag and
drop” languages to program applications for home, school, and office use. Naive
programming will play a crucial role in satisfying the exploding demand for software,
but there will always be limits—just as no hospital patient would submit to a surgery
conducted by a naive surgeon, no customer of a complex or safety-critical system will
settle for software written by anyone but a properly trained, professional programmer.
This textbook is meant for a first course for future professional programmers and
computing scientists.
What makes “professional programming” different from naive programming? The
answer lies in use of structures—control structures (sequencing, conditionals, itera-
tion, parallelism), data structures (arrays, stacks, trees), and component structures
(modules, classes, objects, packages). Professional programmers understand how to
employ properly all three forms of structure; amateurs do not. Further, patterns
of structures-within-structures define architectures that are learned and regularly im-
itated by professionals. As indicated by its title, this text presents standard archi-
tectures for component construction and patterns of control construction and data
construction.
The text takes a “modern” approach by emphasizing component structures over
the other two forms. Computing has matured into a distributed, component-based
activity, where both computer hardware and software are assembled from standard-
ized components and connected together by means of standardized interfaces. By no
means does this text ignore the classic developments of control and data structures—
they appear in due course at an appropriate level of detail. But component-level
issues drive the software design process, and the text emphasizes this fact.
Java is used as the programming language in this text because it supplies solid
support for component-structure-driven design; its control and data structuring mech-
anisms are adequate as well. Because Java and its support library are huge, only a
carefully selected subset that promotes sound programming techniques is presented.

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.

2. Start students sending messages to objects immediately. Amuse and motivate


them with a bit of graphics, but don’t overwhelm them with Java trivia.
iii

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.

5. Finish with interesting applications—GUI-driven programs, database systems,


interactive games, animations, and applets.
The ordering of Step 3 before Step 4 is crucial, because it encourages the component-
driven approach to programming.
Here are some pragmatic issues and how they are handled by the text:
• Design: Beginners learn by imitation. For this reason, the text uses simplified
versions of the Smalltalk Model-View-Controller (MVC) software architecture
for its programs. (The MVC architecture structures a program so that its
model component handles the computational duties of a program, the view
components(s) handle input-output transmission, and the controller controls
the transfer of data from view to model and back.) This architecture helps a
beginner see how a program is assembled from components and how components
are designed so that they can be easily unconnected, reconnected, and replaced.
The beginner can readily imitate this architecture in her own projects.
As part of the design process, components are first specified with UML/Java-
style interfaces before any code is written. (An interface lists the names of the
public methods and private attributes a class needs to do its job.) For complex
applications, UML class diagrams are drawn.

• Documentation: All programs are documented in Sun’s “javadoc” style, and


the reader is shown how to use Sun’s javadoc tool to automatically gener-
ate Web-based documentation pages (the so-called “Application Programming
Interface”—“API”) for her programs. UML class diagrams document the pro-
gram’s overall architecture.

• Pedagogy: Chapters are organized into Essentials-Projects-Supplement compo-


nents. The Essentials sections present the central concepts of that chapter;
included with these sections are exercise sets that guide the student through
basic applications of the topic. The chapter concludes with a section of pro-
gramming projects and multiple Supplement sections, which provide technical
details that a student or instructor can skip the first time through the text.

• Applications: When using Java for programming examples, it is tempting to


emphasize graphics, animations, applets, and networking, for which Java pro-
vides ample support. But not all programming fundamentals are best taught
with these applications, so a middle ground is taken: The text presents a mix-
ture of information processing examples and graphics examples. Animations
iv

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

Chapter 1: Computers and Programming 1


1.1 What is a Computer? 1
1.2 Computer Programming 2
1.3 Programs Are Objects 5
1.4 Operating Systems and Windows 6
1.5 Software Architecture 8
1.5.1 Class Diagrams 9
1.6 Summary 11
1.7 Beyond the Basics 13
1.7.1 Stepwise Refinement 14
1.7.2 Object-Oriented Design 15
1.7.3 Classes Generate Objects 17
1.7.4 Frameworks and Inheritance 18

Chapter 2: Simple Java Applications 20


2.1 An Application and its Architecture 20
2.2 How to Build and Execute an Application 23
2.2.1 Using an IDE 23
2.2.2 Using the JDK 28
2.3 How the Application Works 30
2.3.1 An Execution Trace of the Application 33
2.4 How One Object Constructs Another 35
2.5 Repairing Compiler Error Messages 41
2.6 Summary 43
2.7 Programming Exercises 46
2.8 Beyond the Basics 47
2.8.1 Syntax 47
2.8.2 Semantics 49
2.8.3 Java Packages 51
2.8.4 Java API 51

Chapter 3: Arithmetic and Variables 56


3.1 Integer Arithmetic 57
3.2 Named Quantities: Variables 59
3.2.1 Variables Can Vary: Assignments 63
3.3 Arithmetic with Fractions: Doubles 68
3.4 Booleans 72
3.5 Operator Precedences 74
3.6 Strings, Characters, and their Operations 75
3.7 Data-Type Checking 80
3.8 Input via Program Arguments 83
3.8.1 Converting between Strings and Numbers and Formatting 86
3.8.2 Temperature Conversion with Input 88
3.9 Diagnosing Errors in Expressions and Variables 92
3.10 Java Keywords and Identifiers 95
3.11 Summary 95
3.12 Programming Projects 97
3.13 Beyond the Basics 102
3.13.1 Longs, Bytes, and Floats 102
3.13.2 Helper Methods for Mathematics 103
3.13.3 Syntax and Semantics of Expressions and Variables 105

Chapter 4: Input, Output, and State 107


4.1 Interactive Input 108
4.1.1 Dialog Output 112
4.2 Graphical Output 113
4.2.1 Panels and their Frames 113
4.2.2 Customizing Panels with Inheritance 115
4.3 Format and Methods for Painting 122
4.3.1 Constructor Methods and this Object 124
4.4 Objects with State: Field Variables 130
4.4.1 Using Fields to Remember Inputs and Answers 135
4.4.2 Scope of Variables and Fields 140
4.5 Testing a Program that Uses Input 142
4.6 Summary 144
4.7 Programming Projects 147
4.8 Beyond the Basics 151
4.8.1 Scope of Variables and Fields 151
4.8.2 Partial API for JFrame 152
4.8.3 Methods for GregorianCalendar 152
4.8.4 Colors for Graphics 152
5.8.5 Applets 153

Chapter 5: Component Structure: Method and Class Building 157


5.1 Methods 158
5.2 Public Methods 159
5.2.1 Basic Public Methods 160
5.2.2 Constructor Methods 164
5.3 Parameters to Methods 168
5.3.1 Forms of Parameters 173
5.4 Case Study: General Purpose Output Frame 179
5.5 Results from Methods: Functions 186
5.6 Private Methods 193
5.7 Summary 200
5.8 Programming Projects 203
5.9 Beyond the Basics 212
5.9.1 Naming Variables, Methods, and Classes 212
5.9.2 Generating Web Documentation with javadoc 213
5.9.3 Static Methods 217
5.9.4 How the Java Compiler Checks Typing of Methods 220
5.9.5 Formal Description of Methods 221
5.9.6 Revised Syntax and Semantics of Classes 227

Chapter 6: Control Structure: Conditional Statements 229


6.1 Control Flow and Control Structure 230
6.2 Condtional Control Structure 231
6.2.1 Nested Conditional Statements 235
6.2.2 Syntax Problems with Conditionals 240
6.3 Relational Operations 241
6.4 Uses of Conditionals 245
6.5 Altering Control Flow 249
6.5.1 Exceptions 250
6.5.2 System Exit 251
6.5.3 Premature Method Returns 252
6.6 The Switch Statement 252
6.7 Model and Controller Components 255
6.7.1 Designing an Application with a Model-View-Controller Architecture 257
6.8 Case Study: Bank Accounts Manager 259
6.8.1 Collecting Use-Case Behaviors 259
6.8.2 Selecting a Software Architecture 261
6.8.3 Specifying the Model 261
6.8.4 Writing and Testing the Model 261
6.8.5 Specifying the View Components 265
6.8.6 A Second Look at the Software Architecture 265
6.8.7 Writing the View Classes 265
6.8.8 Controller Construction and Testing 272
6.8.9 Testing the Assembled Application 276
6.8.10 Multiple Objects from the Same Class 277
6.9 More about Testing Methods and Classes 283
6.9.1 Testing Individual Methods 283
6.9.2 Testing Methods and Attributes Together 283
6.9.3 Testing a Suite of Methods 284
6.9.4 Execution Traces 285
6.10 Summary 286
6.11 Programming Projects 290
6.12 Beyond the Basics 295
6.12.1 The Logic of the Conditional Statement 295
6.12.2 Interface Specifications and Integration 300

Chapter 7: Patterns of Repetition: Iteration and Recursion 304


7.1 Repetition 305
7.2 While Loops 306
7.3 Definite Iteration 308
7.3.1 Definite-Iteration Example: Painting a Bulls-Eye 153
7.4 Nontermination 319
7.5 Indefinite Iteration: Input Processing 321
7.5.1 Indefinite Iteration: Searching 324
7.6 For-Statements 328
7.7 Nested Loops 329
7.8 Writing and Testing Loops 335
7.9 Case Study: Bouncing Ball Animation 338
7.10 Recursion 347
7.10.1 An Execution Trace of Recursion 352
7.11 Counting with Recursion 356
7.11.1 Loops and Recursions 359
7.11.2 Counting with Multiple Recursions 360
7.12 Drawing Recursive Pictures 363
7.13 Summary 366
7.14 Programming Projects 370
7.15 Beyond the Basics 378
7.15.1 Loop Termination with break 379
7.15.2 The do-while Loop 380
7.15.3 Loop Invariants 381
7.15.4 Loop Termination 386
7.12.5 More Applets 387

Chapter 8: Data Structure: Arrays 391


8.1 Why We Need Arrays 392

8.2 Collecting Input Data within Arrays 396


8.3 Translation Tables 399
8.4 Internal Structure of One-Dimensional Arrays 402
8.5 Arrays of Objects 406
8.6 Case Study: Databases 3409
8.6.1 Behaviors 412
8.6.2 Architecture 413
8.6.3 Specifications 413
8.6.4 Implementation 415
8.6.5 Forms of Records and Keys 420
8.7 Case Study: Playing Pieces for Card Games 424
8.8 Two-Dimensional Arrays 431
8.9 Internal Structure of Two-Dimensional Arrays 434
8.10 Case Study: Slide-Puzzle Game 437
8.11 Testing Programs with Arrays 446
8.12 Summary 448
8.13 Programming Projects 450
8.14 Beyond the Basics 458
8.14.1 Sorting 458
8.14.2 Searching 462
8.14.3 Time-Complexity Measures 465
8.14.4 Divide-and-Conquer Algorithms 469
8.14.5 Formal Description of Arrays 477

Chapter 9: Programming to Interfaces 483 ¡/a¿


9.1 Why We Need Specifications 484
9.2 Java Interfaces 485
9.2.1 Case Study: Databases 493
9.3 Inheritance 497
9.4 Reference Types, Subtypes, and instanceof 500
9.5 Abstract Classes 508
9.5.1 Case Study: Card Players 509
9.5.2 Class Hierarchies 516
9.5.3 Frameworks and Abstract Classes 519
9.6 Subtypes versus Subclasses 519
9.7 class Object and Wrappers 520
9.8 Packages 522
9.8.1 Generating Package APIs with javadoc 524
9.9 Case Study: An Adventure Game 526
9.9.1 Interfaces and Inheritance Together 532
9.9.2 Inheritance of Interfaces 539
9.10 Summary 539
9.11 Programming Projects 543
9.12 Beyond the Basics 544
9.12.1 Subclasses and Method Overriding 545
9.12.2 Semantics of Overriding 550
9.12.3 final components 555
9.12.4 Method Overloading 556
9.12.5 Semantics of Overloading 561

Chapter 10: Graphical User Interfaces and Event-Driven Programming 564


10.1 Model-View-Controller Revisited 565
10.2 Events 567
10.3 The AWT/Swing Class Hierarchy 568
10.4 Simple Windows: Labels and Buttons 571
10.5 Handling an Event 579
10.5.1 A View as Action Listener 579
10.5.2 A Separate Controller 583
10.5.3 A Button-Controller 583
10.6 Richer Layout: Panels and Borders 590
10.6.1 An Animation in a Panel 594
10.7 Grid Layout 600
10.8 Scrolling Lists 604
10.9 Text Fields 610
10.10 Error Reporting with Dialogs 617
10.11 TextAreas and Menus 620
10.11.1 Case Study: Text Editor 623
10.12 Event-Driven Programming with Observers 632
10.12.1 Observers and the MVC-Architecture 635
10.13 Summary 636
10.14 Programming Projects 641
10.15 Beyond the Basics 645
10.15.1 Applets 646
10.15.2 Tables and Spreadsheets 649
10.15.3 Handling Mouse Clicks and Drags 655
10.15.4 Threads of Execution 664
10.15.5 GUI Design and Use-Cases 669
10.15.6 Summary of Methods for Graphical Components 671

Chapter 11: Text and File Processing 680


11.1 Strings are Immutable Objects 681
11.1.1 String Tokenizers 683
11.2 Sequential Files 686
11.2.1 Output to Sequential Files 688
11.2.2 Input from Sequential Files 690
11.3 Sequential Input from the Command Window 692
11.4 Case Study: Payroll Processing 695
11.5 Exceptions and Exception Handlers 700
11.5.1 Restarting a Method with an Exception Handler 702
11.5.2 Interactive Input with Exception Handlers 705
11.6 Exceptions Are Objects 706
11.6.1 Programmer-Generated Exceptions 713
11.7 Summary 714
11.8 Programming Projects 716
11.9 Beyond the Basics 719
11.9.1 Character-by-Character File Processing 719
11.9.2 Binary Files and Files of Objects 720
11.9.3 A Taxonomy of File Classes 721
11.9.4 A GUI for File Selection 730
Appendix I:Java Language Definition 724

Appendix II:Types and Subtypes 741

Appendix III:Class Diagrams 744

Index 748
Chapter 1

Computers and Programming


1.1 What is a Computer?

1.2 Computer Programming

1.3 Programs Are Objects

1.4 Operating Systems and Windows

1.5 Software Architecture

1.5.1 Class Diagrams

1.6 Summary

1.7 Beyond the Basics

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

2. defining the classes that comprise the architecture

3. writing the Java instructions that realize each class.

1.1 What is a Computer?


Electronic computers can be found almost everywhere, but in general terms, a com-
puter is any entity that can follow orders, more specifically, that can execute instruc-
tions. This classification includes humans (who are imperfect computers) as well as
pocket calculators, programmable disc players, and conventional “PC”s.
This text is concerned with conventional computers, which must possess (at least)
these two components:

• one (or more) processors. A processor executes instructions (e.g., arithmetic


calculations or instructions to draw colors and shapes).

• primary storage (also known as “memory,” “random access memory,” or “RAM”).


Primary storage acts as an electronic “scratch pad” that holds the instruc-
tions that the processor reads and executes and holds numbers and information
(“data”) that the processor calculates upon—primary storage is like the scratch
pad that an accountant (a human “processor”) uses.
2

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?

1.2 Computer Programming


Programming is the activity of writing instructions that a computer can execute. For
1.2. COMPUTER PROGRAMMING 3

Figure 1.1: a program for baking “lemon cake”


1. Squeeze the juice from one lemon into a large bowl.
2. Cut the lemon’s rinds into small pieces and add them to the bowl.
3. Mix three eggs, one cup of sugar, two tablespoons of flour,
and one cup of milk into the bowl.
4. Pour the mixture into a square cake pan.
5. Heat an oven to 350 degrees Fahrenheit.
6. Insert the pan into the oven and wait 40 minutes.

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:

1. First, a translator program, called a compiler, is used to translate Java programs


4

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

Figure 1.2: the computing environment

DISPLAY DISKS

USER EXECUTING PROGRAM (in primary storage)

KEYBOARD OTHER COMPUTERS

(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);

Which parts of this program appear to be verbs? nouns? adjectives? algebraic


expressions?

5. Propose a programming language for drawing colored bubbles and squares on


a sheet of paper. What verbs will you include (e.g., “draw,” “trace”)? nouns
(“circle,” “red”)? adjectives (“large,” “dark”)?

1.3 Programs Are Objects


When a computer program is executing (that is, the program has been copied into
primary storage, and the processor is executing its instructions), the program does
not “live” in isolation; it is surrounded by an environment consisting of a keyboard,
display, disks, and even other computers. Figure 2 pictures such an environment. We
use the term, object, as a generic term for each of the components in a computing en-
vironment. The EXECUTING PROGRAM is itself an “object,” as is the computer’s
USER (a human being), who interacts with the machinery. Indeed, a program can
itself consists of multiple objects (smaller programs).
An “object” is meant to be a generalization of a “person”—an object has an
“identity,” it knows some things, and it is able to do some things.
We use the term, method, to describe an object’s skill or ability or activity—a
thing that an object can do. For example, an executing word-processing program is
an object that has methods for inserting words, moving words, correcting spelling
errors, and typesetting documents. An executing program that calculates retirement
savings is an object with methods for calculating rates of savings, interest rates,
6

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.

1.4 Operating Systems and Windows


When a computer is first started, its processor looks on the computer’s disk drive
for a program to place first into primary storage to execute; the program it finds
is called the operating system. An operating system is the computer’s “controller
program”; by displaying information on the display and by receiving messages from
mouse and keyboard, the operating system helps the computer’s user execute other
programs. Often, a user’s request to execute a program is little more than a mouse
movement and a click, or it might be the typing of text within a command window
1.4. OPERATING SYSTEMS AND WINDOWS 7

Figure 1.3: a multi-window display

(or command-prompt window).


Prior to the 1980s, an operating system used the computer’s display as one large
command window. Programs were started by typing within the window, and the
program would read its input data from the window and would display its output
within the same window. All input and output were text—words and numbers.
Of course, modern operating systems create multiple windows on the display,
where the windows might be command windows or windows created by executing
programs. Figure 3 shows a display that holds three distinct windows: a command
window, a window created by a word-processing program, and a window created by a
Web-browsing program. These windows were created with the behind-the-scenes help
of the operating system. The user interacts with the window by moving the mouse
into it, typing, clicking, or reading. Icons appear along the left side of the display.
The point of Figure 3 is that the multiple windows that appear on the display
are themselves distinct objects. The crude picture in Figure 2 suggested that the
DISPLAY was one object, but modern operating systems make it possible for an
8

executing program to communicate with multiple window objects. In a similar way,


the multiple folders and files that reside in secondary storage are also distinct objects.

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.

1.5 Software Architecture


The previous section stated that a typical computing environment contains many ob-
jects: keyboard, windows, executing program, files on secondary storage, etc. When
we do some computing, we do not build objects like keyboards from scratch—we use
the one we bought. And we buy programs and use them as well. But this text is
about building programs rather than buying them—how do we do this?
Becoming a good programmer is not unlike becoming a good novelist, bridge-
building engineer, or architect—time and effort must be invested in studying standard
examples of the genre, disassembling and modifying them, learning basic techniques
of composition/construction, and working plenty of exercises.
In novel writing, bridge construction, and house building, a beginner is tempted
to start on the final product without first investing time in a design. A professional
knows better. Consider house building: A modern house’s design is specified by a
blueprint. A blueprint possesses a “style” or architecture. A house can have a simple,
one-room architecture, or it can have a multi-room, split-level, architecture, or it
might even be multiple housing units connected by passageways and stairways.
The house builder uses the blueprint to construct a physical structure with walls,
floors, electrical wiring, and plumbing. Almost no one would buy a house that was
not constructed from a blueprint—the house would not be trustworthy.
Builders read blueprints; most do not design them. The highly trained individual
who designs and draws the blueprint is an architect. A well trained architect knows
about mathematics, physics, and art as well as house construction.
Machines, such as cars, automated bank tellers, and computers, also have architec-
tures (see Figure 2), and so do computer programs. Writing a computer program in-
volves more than merely writing instructions in Java—one must design the program’s
architecture, specify the architecture’s components, and write Java instructions for
each component. Here are the crucial steps:

• First, a program’s architecture is specified by drawing a picture called a class


diagram. The class diagram indicates the components, called classes, that form
the entire program, much like a blueprint shows the rooms that form a house.
1.5. SOFTWARE ARCHITECTURE 9

• Next, each class is designed as a collection of methods.

• 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.

• A controller object—it has methods that control the transfer of information


within the ATM by sending messages to the other components.

• A model object—its methods compute answers in response to messages sent to


it by the controller.

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.

1.5.1 Class Diagrams


Real programs consist of thousands of instructions and are too huge to be written
as one long file. As noted in the previous section, a program must be divided into
manageably-sized files called classes. Each class’s instructions are themselves grouped
into meaningful subcollections, called methods—we use the term, “method,” as in the
10

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

Figure 1.4: a class diagram of a word processor

Controller
main

Input View Model Output View


getRequest insertText redisplayDocument
getText deleteText printDocument
reformatLines
...
contentsOf

and class diagrams to guide our way.

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

• processor: the part of an electronic computer 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.

• secondary storage: the parts of an electronic computer that archives additional


instructions and information. Examples are the internal “hard” disk, diskettes,
and compact disks. For the processor to read the information on secondary
storage, it must be copied into primary storage.

• input device: a mouse or keyboard, which supplies information to a computer

• output device: a display screen or printer, which presents information to a com-


puter’s user

• file: a collection of related information, typically saved on secondary storage

• folder: a collection of files, typically saved on secondary storage


12

• bit: the “atomic” unit of information within a computer—a “1” or a “0”


• binary code: a sequence of bits, read by the processor as instructions or infor-
mation.
• machine language: the specific format of binary code read by a specific processor
• algorithm: a sequence of declarative instructions (“orders”) for accomplishing a
task.
• computer program: an algorithm written specifically for a processor to execute.
• code: traditional name for a computer program
• programming language: a language designed specifically for writing computer
programs
• compiler: a computer program, that when executed, translates computer pro-
grams in one programming language into programs in another language
• interpreter: a computer program, that when executed, helps a processor read
and execute computer programs that are not written in machine language
• Java byte code: the compiler for the Java programming language translates
programs written in Java into programs in Java byte code
• Java virtual machine (JVM): the interpreter for Java byte code
• object: a basic unit of an executing computer program
• method: an ability that an object possesses
• message: a communication that one object sends to another
• client object: an object that sends a message to a receiver
• receiver object: an object that receives a message sent by a client
• input: information given to a computer program for computation
• output: the answers computed by a program
• hardware: the physical components of a computer, e.g., processor and primary
storage
• software: computer programs
• operating system: the controller program that starts when a computer is first
switched on
1.7. BEYOND THE BASICS 13

• command window: a position on the computer display where a human can type
instructions to a computer

• software architecture: the overall design of a computer program; analogous to a


house’s blueprint

• class diagram: a graphical presentation of a program’s architecture

• class: a file containing a component of a computer program

• Model-View-Controller (MVC) architecture: a standard software architecture

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.

• A computer program is saved in a file in secondary storage. When the program


is started, it is copied into primary storage, and the processor executes the
instructions. We say that the program is executing.

• 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.

1.7 Beyond the Basics


1.7.1 Stepwise Refinement

1.7.2 Object-Oriented Design and Refinement

1.7.3 Classes Generate Objects

1.7.4 Frameworks and Inheritance

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

1.7.1 Stepwise Refinement


In the cooking world, Figure 1 is a “method” for baking lemon cake, and an Italian
cookbook of is a collection, or “class,” of such methods.
How do we write a method from scratch? A classic methodology for method
writing is stepwise refinement (also known as top-down design), which is the process
of writing an outline of a method and then refining the outline with more and more
precise details until the completed method is the final result.
For example, perhaps we must write a method for making lasagna. We begin with
an outline of the basic steps:

1. prepare the sauce

2. cook the pasta

3. place the pasta and sauce in a dish; cover with topping

4. bake the filled dish

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.1 Take a medium-sized dish.

• 3.2 Cover the bottom of the dish with sauce.

• 3.3 Next, place a layer of pasta noodles on top of the sauce.

• 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:

1. how to journey from your home to the airport

2. how to make your favorite pizza

3. how to change a flat tire on an automobile

4. how to add two fractions

5. how to perform the long division of one integer by another

6. how to calculate the sales tax (value-added tax) for a purchase in your commu-
nity

1.7.2 Object-Oriented Design


A good programmer will rely on standard architectural patterns, like the MVC ar-
chitecture seen in this chapter, as much as possible. But occasionally a part of an
architecture (e.g., the MODEL portion of Figure 4) or even a complete new architec-
ture must be designed specially. We encounter such situations in Chapter 7 onwards.
Object-oriented design can be used to design an architecture. Object-oriented
design solves a problem by treating it as a simulation—a re-creation of reality—
inside the computer. For example, games are simulations: When a child pretends
to be a doctor or a fireman, she is creating a simulation. War games like chess and
“battleship” are also simulations, and it is no accident that such games are available
as computer programs.
Indeed, computers are excellent devices for conducting simulations. Executing
programs can create representations of people, televisions, airplanes, and even the
weather. Computer simulations have proven valuable for studying traffic circulation
in cities, predicting climate changes, and simulating the behavior of airplanes in flight.
Some people argue that all computing activities are simulations, because a computer
itself is a “simulation” of how a human being behaves when she calculates.
What is the relationship of simulation to program design? A simulation has
actors or “objects” (e.g., people or pawns or battleships) who play roles. Each actor
has abilities or “methods,” and the actors communicate and interact to enact the
simulation.
To gain some intuition about simulations, here an example. Say that you are
hired as a consultant to help some entrepreneurs start a new Italian restaurant, and
you must design an operating plan for the the restaurant and its personnel.
To solve this nontrivial problem, you simulate the restaurant and its personnel.
The simulation is formed by answering several basic questions:
16

Figure 1.5: architecture of a restaurant

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)?

• Collaborators: What will be the patterns of communication between the ob-


jects? (Who talks with whom in 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

Figure 1.6: model part of a more realistic word processor

Document Clipboard
getText saveClippedText
deleteText retrieveClippedText
reformatLines
...
getHighlightedText
contentsOf Formatter
setTypeFont
getTypeFont
setLineLength
getLineLength

object-oriented design. For example, the MODEL portion of the word-processor


architecture in Figure 4 is simplistic and can be replaced by one developed with
object-oriented design; see Figure 6.
The Figure presents the model as consisting of a DOCUMENT component, which
has the responsibility of holding the typed text and managing insertions and dele-
tions. To help it with its responsibilities, the DOCUMENT sends messages to a
FORMATTER, which manages font and line-length information. A third component,
the CLIPBOARD, handles cut-and-paste actions. It takes only a little imagination
to see that the DOCUMENT, FORMATTER, and CLIPBOARD are behaving much
like the CUSTOMER, WAITER, and COOK seen earlier—actors communicating and
interacting to accomplish a task.
In this manner, we can employ object-oriented design to creatively organize a
program into distinct components with distinct responsibilities.

Exercises
Use object-oriented design to model the following:
1. a hamburger stand

2. a grocery store

3. an airport

4. a computer program that calculates spreadsheets

1.7.3 Classes Generate Objects


The class diagram in Figure 5 presented an architecture for an Italian restaurant.
If we constructed a real restaurant from the diagram, however, we would certainly
employ more than one waiter and one cook and we would certainly hope for more
than one customer. From the designer’s perspective, multiple waiter “objects” can
18

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.

1.7.4 Frameworks and Inheritance


If we find ourselves designing lots of markets and restaurants, we might develop a set
of classes and a basic architecture that can be used, with minor variations, over and
over again. Such a collection is called a framework.
Frameworks often depend on inheritance to do their job. Inheritance lets us add
minor customizations to a class. For example, our design of a basic CASHIER might
need some customization to fit perfectly into the Italian restaurant, say, perhaps
the cashier must speak Italian. We customize the CASHIER class by writing the
new method, speakItalian, and inventing new name, say, ITALIAN CASHIER, for
the customized class that possesses all the methods of the CASHIER plus the new
method. The situation we have in mind is drawn like this in class-diagram style:

Italian Cashier Cashier


speakItalian receiveMoney

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

We employ inheritance in Chapter 4 to generate graphics windows.


Another standard way of explaining inheritance is in terms of the “is-a” relation-
ship, e.g., “An Italian-restaurant cashier is a cashier who can speak Italian.” Such
examples of inheritance abound in zoology, and here is one informal example:

• A mammal is an animal that is warm-blooded.

• A feline is a mammal that is cat-like.

• A tiger is a feline that has stripes.

• A lion is a feline that has a mane.

• A giraffe is a mammal that has an extremely long neck.

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

Simple Java Applications


2.1 An Application and its Architecture

2.2 How to Build and Execute an Application

2.2.1 Using an IDE

2.2.2 Using the JDK

2.3 How the Application Works

2.3.1 An Execution Trace of the Application

2.4 How One Object Constructs Another

2.5 Repairing Compiler Error Messages

2.6 Summary

2.7 Programming Exercises

2.8 Beyond the Basics

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.

2.1 An Application and its Architecture


If you were asked to built a robot, what would you do? First, you would draw a picture
of the robot; next you would write the detailed instructions for its assembly. Finally,
you would use the detailed instructions to build the physical robot. The final product,
a robot “object,” has buttons on its chest, that when pushed, cause the robot to talk,
walk, sit, and so forth. The buttons trigger the robot’s “methods”—the activities the
robot can perform.
2.1. AN APPLICATION AND ITS ARCHITECTURE 21

Figure 2.1: architecture of initial application

Hello [Note: what we will write]


main

System.out
println [Note: Java’s name for the command window]

As we learned in the previous chapter, program construction follows the same


methodology: We draw an initial design, the class diagram, and for each component
(class) in the diagram, we write detailed instructions in Java, which are saved in a file
in secondary storage. When we start the program, the class is copied into primary
storage and becomes an executing object.
The Java designers use the term application to describe a program that is started
by a human user. After an application’s object is constructed in primary storage, a
message is automatically sent to the application’s main method, causing its instruc-
tions to execute. Therefore, every Java application must possess a method named
main.
Begin Footnote: Not surprisingly, the precise explanation of how an application
starts execution is more complex than what is stated here, but we nonetheless stick
with our slogan that “classes are copied into primary storage and become executing
objects.” End Footnote
To illustrate these ideas, we construct a small Java application that contains just
the one method, main, which makes these two lines of text appear in 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

Figure 2.2: sample application


/** Hello prints two lines in the command window */
public class Hello
{ public static void main(String[] args)
{ System.out.println("Hello to you!");
System.out.println(49);
}
}

2.2 How to Build and Execute an Application


We take several steps to make the application in Figure 2 print its text in the command
window:

1. class Hello must be typed and saved in the file, Hello.java.

2. The program’s spelling and grammar must be checked by the Java compiler;
that is, the program must be compiled.

3. The program must be started (executed).

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.

2.2.1 Using an IDE


There are many IDEs available for Java, and we cannot consider them all. Fortunately,
IDEs operate similarly, so we present a hypothetical example. Be certain to read the
manual for your IDE before you attempt the following experiment.
When an IDE is started, it will present a window into which you can type a Java
application (or “project,” as the IDE might call it). Select the IDE’s menu item or
button named New Project to create a new project, and when the IDE asks, type
the name of the class you wish to write. For the example in Figure 2, use the name,
24

Hello, for the project:


2.2. HOW TO BUILD AND EXECUTE AN APPLICATION 25

Next, type the class into the window and save it by using the IDE’s Save button:

If done properly, class Hello will be saved in a file named Hello.java.

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.)

2.2.2 Using the JDK


An alternative to an IDE is a text editor and the Java Development Kit (JDK). Your
computer should already have a text editor (e.g., Notepad or emacs or vi) that you
can use to type and save files. The JDK can be obtained from Sun Microsystems’s
Web Site at http://java.sun.com. You must download the JDK and properly install
it on your computer; installation is not covered here, but instructions come with the
JDK.
Once you have the JDK installed, the first step is to use a text editor to create
the file, Hello.java, with the contents of Figure 2:
2.2. HOW TO BUILD AND EXECUTE AN APPLICATION 29

Next, compile the application by typing in a command window, javac Hello.java:

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.

2.3 How the Application Works


We now scrutinize Figure 2 line by line. The class’s first line,
/** Hello prints two lines in the command window */

is a comment. A comment is not a Java instruction for the computer to execute—it


is a sentence inserted, as an aside, for a human to read, in the case that the human
wants to inspect the application before it is compiled and executed. The comment is
meant to explain the purpose of class Hello. A comment can extend over multiple
lines, as we will see in later examples.
You should begin every class you write with a comment that explains the class’s
purpose. Java programs are not all that easy for humans to read, and a few lines of
explanation can prove very helpful!
2.3. HOW THE APPLICATION WORKS 31

Begin Footnote: By the way, it confuses the Java compiler if you place one com-
ment inside another, e.g.

/** A badly formed /** comment */ looks like this. */

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:

public class Hello


{
...
}

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:

public class Hello {


...
}

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:

public static void main(String[] args)


{
...
}
32

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);

Each of the statements has the form, System.out.println(SOMETHING TO DISPLAY);


this statement form sends a message to the System.out object, and the message is
println(SOMETHING TO DISPLAY). The SOMETHING TO DISPLAY-part is the argument part
of the message. The statement must be terminated by a semicolon. (Semicolons in
Java are used much like periods in English are used.)
This message asks System.out to execute its println method. The instructions
inside the println method display the argument, SOMETHING TO DISPLAY, at the posi-
tion of the cursor in the command window. And, println inserts a newline character
immediately after the displayed argument—this makes the cursor in the command
window move to the start of a fresh line.
As we learn in the next chapter, the Java language requires that textual phrases
must be enclosed within double quotes, e.g., "Hello to you!". Textual phrases are
called strings. The double quotes that surround strings prevent confusing strings with
keywords. For example, "public class" is clearly a string and not two keywords side
by side. In contrast, numbers like 49 are not strings and are not enclosed by double
quotes.
Begin Footnote: But note that "49" is a string, because of the double quotes. The
distinction between 49 and "49" becomes clear in the next chapter. End Footnote
Method println serves as System.out’s “communication” or “connection” point,
and the Hello object makes two such communications with System.out by using
println, not unlike contacting a friend twice on the telephone.
Here is a final point, about spelling: The Java language distinguishes between
upper-case and lower-case letters. Therefore, public is spelled differently from PUBLIC
and Public, and only the first is accepted as the correct Java keyword. It is traditional
to spell the names of classes beginning with a single upper-case letter, e.g., Hello, and
method names tranditionally begin with a lower-case letter, e.g., println. Additional
spelling guidelines are introduced in the next chapter.

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.)

2.3.1 An Execution Trace of the Application


When we demonstrated the Hello application, we saw the lines, Hello to you! and
49 appear in the command window, but we did not see how the computer executed the
application’s instructions to produce these results. To be a competent programmer,
you must develop mental images of how the computer executes programs. To help
develop this skill, we study a step-by-step pictorial re-creation—an execution trace—of
the computer’s actions.
When the Hello application is started by a user, the file, Hello.class, is copied
into primary storage and becomes a Hello object.
Begin Footnote: The preceding explanation is imprecise, because it fails to explain
that the “object” created from Hello.class is not the usual form of object described
in Chapter 1. But it is not worth the pain at this point to state the distinction
between a class that possesses an invoked static main method and one that possesses
an invoked non-static constructor method. Our situation is similar to the one in an
introductory physics course, where Newtonian physics is learned initially because it
is simple and beautiful although slightly incorrect. In subsequent chapters, these
technical issues are resolved. End Footnote
Here is a depiction of the Hello object (and System.out, which already exists) in
primary storage:
Hello System.out
...
public static void main(String[] args)
println(...)
{ System.out.println("Hello to you!");
{ instructions to print text }
System.out.println(49);
}

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

The statement, System.out.println("Hello to you!"), sends the message, println("Hello


to you!"), to the System.out object. The argument, "Hello to you!", is what the
method will print. (The enclosing double quotes are not printed, but you must in-
clude them, nonetheless.) This is an example of how one object sends a message to
another object, awakening it from its rest.
The message is delivered to System.out, which puts its println method to work.
In the interim, the Hello object waits:
Hello System.out
...
public static void main(String[] args)
println(...)
{ > AWAIT System.out’s COMPLETION
{ > instructions to print text }
System.out.println(49);
}

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
}

Once println finishes, there is nothing more to execute in main’s body,


Hello System.out
...
public static void main(String[] args)
println(...)
{ ...
{ instructions to print text }
...
> }

and Hello signals that it is finished.

Exercise
Write an execution trace for a program you wrote from the earlier Exercise set.
2.4. HOW ONE OBJECT CONSTRUCTS ANOTHER 35

Figure 2.3: class diagram of an application that displays the time

NameAndDate GregorianCalendar [Note: connects to the


main getTime computer’s internal clock]

System.out
println
print

2.4 How One Object Constructs Another


The Hello example showed how an object can send messages to the preexisting object,
System.out. But it is also possible for an object to construct its own, “helper,” objects
as needed and send them messages. We see this in an example.
Say that you want an application that prints your name and the exact date and
time, all on one line, in the command window. Of course, the date and time are depen-
dent on when the program starts, and you cannot guess this in advance. Fortunately,
the Java designers wrote a class, named class GregorianCalendar, that knows how
to read the computer’s internal clock. So, we construct an object that itself constructs
a GregorianCalendar object and sends the GregorianCalendar object a message for
the exact date and time. Figure 3 shows the architecture of the application we plan
to build. The class we write is called NameAndDate. It will send a getTime message
to the GregorianCalendar and then it will send messages to System.out to print the
time. (The new method, print, of System.out, is explained below.)
When this program is executed, we will see in the command window something
like this:

Fred Mertz --- Fri Aug 13 19:07:42 CDT 2010

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:

GregorianCalendar c = new GregorianCalendar();

Here, the name is c. (The extra word, GregorianCalendar, is explained later.)


We can use the name c like we used the name, System.out—we can send mes-
sages to the named object.
• Send object c a getTime message that asks it to consult the clock and reply
with the date and time:

c.getTime()

• Tell System.out to display this date and time:

System.out.println(c.getTime());

Recall that the statement, System.out.println(ARGUMENT), prints the ARGUMENT.


Here, we insert, c.getTime(), which asks c to reply with the time as a response—
it is the time received in response that is printed.
Figure 4 shows the program that uses the above steps. In addition to what we
have just studied, several other new techniques are illustrated in the example. We
explain them one by one.
To understand the first line,
import java.util.*;

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

Figure 2.4: application that prints the date and time


import java.util.*;
/** NameAndDate prints my name and the exact date and time. */
public class NameAndDate
{ public static void main(String[] args)
{ System.out.print("Fred Mertz --- ");
// The next statement creates an object:
GregorianCalendar c = new GregorianCalendar();
System.out.println(c.getTime()); // ask c the time and print its reply
System.out.println();
System.out.println("Finished");
}
}

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 --- ");

Method print is another method that belongs to System.out. When executed,


print(ARGUMENT) displays the ARGUMENT at the position of the cursor in the com-
mand window, but no newline character is appended immediately after the displayed
argument—this retains the command window’s cursor on the same line as the dis-
played argument so that additional information can be printed on the line.
The line,
// The next statement creates an object:

is an internal comment. An internal comment is a comment inserted inside a class,


providing technical information to the person who is studying the program. An
internal comment extends only from the double slashes until the end of the line on
which it appears. In this text, we use internal comments to explain the workings of
important or subtle statements.
The statement,
GregorianCalendar c = new GregorianCalendar();
38

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()

prints an argument that is nothing, to which is appended a newline character. The


net effect is that the cursor in the command window is moved to the beginning of a
fresh line. (The parentheses, (), hold no information to print.)

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

We see the following:

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

message to execute the instructions in its getTime method:


NameAndDate System.out
main
...
{ ... // the test, "FredMertz --- " has been displayed
GregorianCalendar c == a1
> System.out.println( AWAIT THE REPLY FROM a1 );
System.out.println();
System.out.println("Finished");
}

a1 : a GregorianCalendar object
...
getTime()
{ > a method that reads the clock and returns the time }

The main method waits while a1’s getTime method executes.


Once getTime gets the date and time from the computer’s clock, it returns the
date and time to the exact position from which the message was sent:
NameAndDate System.out
main
{ ... // the test, "FredMertz --- " has been displayed ...
GregorianCalendar c == a1
> System.out.println("Fri Aug 13 19:07:42 CDT 2010" );
System.out.println();
System.out.println("Finished");
}

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

public class NameAndDate


{ public static void main(String[] args)
{ System.out.print("Fred Mertz --- ");
// The next statement constructs an object and sends it a getTime message:
System.out.println(new GregorianCalendar().getTime());
System.out.println();
System.out.println("Finished");
}
}

The key statement,

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.

2.5 Repairing Compiler Error Messages


Humans can tolerate errors in spelling and grammar, but computers cannot, and
for this reason, the Java compiler complains if a program contains any spelling or
grammar errors. The compiler will identify the line on which an error appears and
give a short explanation.
For example, here is a program that contains several errors. (Take a moment to
locate them.)

public class test


{ public static main(String[] args)
{ System.out.println(Hello!)
}

Perhaps we save this program in Test.java and compile it. The compiler replies with
several error messages, the first of which is

Test.java:2: Invalid method declaration; return type required.


{ public static main(String[] args)
^
42

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:

Test.java:3: ’)’ expected.


{ System.out.println(Hello!)
^

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:

Test.java:4: ’}’ expected.


}
^

This time, the explanation is on target—we are indeed lacking a closing bracket.
Finally, the compiler reports an error at Line 1:

Test.java:1: Public class test must be defined in a file called "test.java".


public class test
^

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:

public class Test


{ public static void main(String[] args)
{ System.out.println("Hello!") }
}

When we compile the revised program, the compiler identifies one error we missed:

Test.java:3: ’;’ expected.


{ System.out.println("Hello!") }
^

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):

public class Hello


{
...
}

• main method (from Figure 2):

public static void main(String[] args)


{
...
}
44

• comment (from Figure 2):

/** Hello prints two lines in the command window */

• internal comment (from Figure 4):

// The next statement creates an object:

• message-sending statement (from Figure 2):

System.out.println("Hello to you!");

• constructing a new object (from Figure 4):

new GregorianCalendar()

• variable name (from Figure 4):

GregorianCalendar c = 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.

• statement: a single instruction within a Java program; typically terminated by


a semicolon.

• main method: the “start up” method that executes first when an application is
started.

• argument: extra information that is appended to a message sent to an object;


enclosed within parentheses following the method name within the message.

• comment: an explanation inserted into a program for a human (and not the
computer) to read.

• string: a sequence of characters, enclosed by double quotes, e.g., "hello".

• Java Development Kit (JDK): a collection of programs, available from Sun


Microsystems, that let a user compile and start a Java application from the
command window.
2.6. SUMMARY 45

• integrated development environment (IDE): a single program from which a use


can edit, compile, start, and monitor a Java application
• execution trace: a sequence of pictures of primary storage, showing the changes
to the objects as the computer executes statements one by one.
• address (of an object): an object’s “name” within primary storage; used to send
messages to it.
• variable name: a name of a cell where a value, such as the address of an object,
is saved. (See Chapter 3 for a fuller development.)

Additional Points to Remember


• A Java application must be compiled before it can be started. When a file,
C.java, is compiled, another file, C.class is created, and objects are constructed
from C.class when the user starts C. A message is sent to object C’s main
method.
• When an application is created in primary storage, there are already other
preexisting objects, such as System.out, to which messages can be sent.
• An object can create other objects by stating the keyword, new, followed by the
name of the class from which the object is to be constructed.
• When an object is created from a class that is located in a Java package, the
package’s name should be explicitly imported so that the Java interpreter can lo-
cate the class. For example, class GregorianCalendar is located in the package,
java.util, so we import the package to use the class:

import java.util.*;
public class ExampleOfImportation
{
... new GregorianCalendar() ...
}

New Constructions for Later Use


• System.out: the object that communicates with the command window. Meth-
ods:
– print(ARGUMENT): displays ARGUMENT in the command window at the posi-
tion of the cursor
– println(ARGUMENT): displays ARGUMENT, appended to a newline character,
in the command window at the position of the cursor.
46

• class GregorianCalendar (a class from which one can construct objects that
read the computer’s clock). Found in the package, java.util. Method:

– getTime(): returns the current time

2.7 Programming Exercises


1. Write a Java program that displays your name and postal address on three or
more lines. Next, use a GregorianCalendar object to print the date and time
before your name.

2. Write a Java program that appears to do nothing at all.

3. Create two GregorianCalendar objects by inserting these two statements,

System.out.println( new GregorianCalendar().getTime() );


System.out.println( new GregorianCalendar().getTime() );

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?

6. Write a Java program that draws this picture on the display:

/\
/ \
----
| - |
| | ||
2.8. BEYOND THE BASICS 47

2.8 Beyond the Basics


2.8.1 Syntax

2.8.2 Semantics

2.8.3 Java Packages

2.8.4 Java API

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:

CLASS ::= public class IDENTIFIER { METHOD* }

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* }

The body of a method is a sequence of zero or more STATEMENTs enclosed by brackets.

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

OBJECT_CONSTRUCTION ::= new IDENTIFIER ( ARGUMENT_LIST? )

An OBJECT CONSTRUCTION begins with the keyword, new, followed by an IDENTIFIER


that is the name of a class. Brackets enclose an optional ARGUMENT LIST.
Messages to objects and newly constructed objects can use ARGUMENT LISTs:

ARGUMENT_LIST ::= EXPRESSION [[ , EXPRESSION ]]*


EXPRESSION ::= LITERAL | INVOCATION

An ARGUMENT LIST is a sequence of one or more EXPRESSIONs, separated by commas.


(The double brackets, [[ and ]], indicate that the * groups both the comma and the
EXPRESSION.
For example, say that "hello" and 49 are both well-formed LITERALs. Then,
both are well-formed EXPRESSIONs, and this means "hello", 49 is a well-formed
ARGUMENT LIST
All the examples in this chapter used ARGUMENT LISTs that consisted of zero (e.g.,
System.out.println()) or just one (e.g., System.out.println(new GregorianCalendar().
getTime())) EXPRESSIONs.
Within Chapter 2, an EXPRESSION was either a LITERAL, like "hello", or an
INVOCATION (new GregorianCalendar().getTime()).

Literal and Identifier

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:

public static void METHOD_HEADER METHOD_BODY

(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

2.8.3 Java Packages


It is painful to write any computer program completely “from scratch” and it is better
to use already-written objects and classes to simplify the task. We did this in the ex-
amples in this chapter when we used the System.out object and the GregorianCalendar
class to help us display text in the command window and ask the computer’s clock
the time.
Objects and classes like these two are organized into folders called packages.
System.out lives in a package named java.lang, and GregorianCalendar appears
in java.util. The former package, java.lang, contains many objects and classes
that are essential for basic programming. Indeed, java.lang contains those Java-
components that represent the “computing environment” portrayed in Figure 2 in
Chapter 1. For this reason, every Java application automatically gets use of the
objects and classes in java.lang.
But there are many other Java packages—for graphics, networking, disk-file ma-
nipulation, general utilities—and if an application wishes to use a component from
one of these packages, then that package must be explicitly imported for the use
of the application. We saw this in Figure 4, where we stated import java.util.*
at the beginning of class NameAndDate so that class GregorianCalendar could be
found. (java.util is the general utility package; it has classes that can create clocks,
calendars, and other structures. In Chapter 4, we will import the java.awt and
javax.swing packages, whose classes know how to draw graphics on the display.)

2.8.4 Java API


There are literally hundreds of objects and classes organized into Java’s two dozen
packages. How do we learn the names of these components and how to use them?
It is a bit early to undertake this ambitious task, but we can at least learn where to
look for basic information—we read Java’s Application Programming Interface (API)
documentation.
The Java API documentation is a summary of the contents of the packages; it is
a bit like a dictionary, where each word is listed with its proper spelling and a one-
sentence description of its use. The Java API documentation lists the components of
its packages and gives short explanations about how to use the components.
Fortunately, the Java API documentation is organized as a collection of Web pages.
It is best to download into your computer a complete set of the API’s web pages;
see http://java.sun.com for details about “Documentation.” But you are welcome
to survey the API documentation at Sun’s Web site at the URL just mentioned, as
well.
Let’s make a quick search for information about the System.out object. We begin
52

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

its link, we see

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

Arithmetic and Variables


3.1 Integer Arithmetic

3.2 Named Quantities: Variables

3.2.1 Variables Can Vary: Assignments

3.3 Arithmetic with Fractions: Doubles

3.4 Booleans

3.5 Operator Precedences

3.6 Strings, Characters, and their Operations

3.7 Data-Type Checking

3.8 Input via Program Arguments

3.8.1 Converting between Strings and Numbers and Formatting

3.8.2 Temperature Conversion with Input

3.9 Diagnosing Errors in Expressions and Variables

3.10 Java Keywords and Identifiers

3.11 Summary

3.12 Programming Projects

3.13 Beyond the Basics

Traditional computer programs calculate: They calculate monthly payments on a


loan, compute the roots of a quadratic equation, or convert dollars into euros. Such
calculations rely on two concepts from algebra—arithmetic operations and variables—
and this chapter develops both as they appear in programming.
The chapter’s objectives are to
• Review the principles of arithmetic computation and introduce the notion of
data type, which ensures consistency within computations.

• Introduce computer variables and their traditional uses.

• Provide a simplistic but effective means to supply input data to a program,


so that one program can be used over and over to perform a family of related
calculations.
3.1. INTEGER ARITHMETIC 57

3.1 Integer Arithmetic


An electronic computer is an “overgrown” pocket calculator, so it is no surprise that
we can write computer programs that tell a computer to calculate on numbers. We
will learn to write such programs in Java by studying a classic example: calculating
the value of the coins in your pocket.
How do you calculate the value of your spare change? No doubt, you separate
into groups your quarter coins (25-cent pieces), dimes (10-cent pieces), nickels (5-
cent pieces), and pennies (1-cent pieces). Once you count the quantity of each, you
multiple each quantity by the value of the coin and total the amounts.
For example, if you have 9 quarters, 2 dimes, no nickels, and 6 pennies, you would
calculate this total:

(9 times 25) plus (2 times 10) plus (0 times 5) plus (6 times 1)

which totals to 251 cents, that is, $2.51.


If your arithmetic skills are weak, you can embed the above computation into a
simple Java program and have the computer do it for you, because the Java language
lets you write arithmetic expressions like 9 * 25 (the * means multiplication) and (9
* 25) + (2 * 10) (the + means addition), and so on—symbols like * and + are called
operators, and the operators’ arguments (e.g., 9 and 25 in 9 * 25) are their operands.
Here is the program that does the calculation:

/** Total computes the amount of change I have, based on


* 9 quarters, 2 dimes, no nickels, and 6 pennies */
public class Total
{ public static void main(String[] args)
{ System.out.println("For 9 quarters, 2 dimes, no nickels, and 6 pennies,");
System.out.print("the total is ");
System.out.println( (9 * 25) + (2 * 10) + (0 * 5) + (6 * 1) );
}
}

This program prints in the command window:

For 9 quarters, 2 dimes, no nickels, and 6 pennies,


the total is 251

Of course, the crucial statement is

System.out.println( (9 * 25) + (2 * 10) + (0 * 5) + (6 * 1) );

which embeds the arithmetic calculation as the argument to System.out’s println


method. As we discovered in the previous chapter, the argument part of a message
is always calculated to its answer before the message is sent. In the present case, it
58

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

3.2 Named Quantities: Variables


You will not enjoy using the previous change-counting program, Total, over and over,
because it will be difficult to alter it each time you come home with a new set of coins
in your pocket. We can improve the situation by giving names to the quantities of
quarters, dimes, nickels, and pennies. These names are called computer variables, or
variables for short.
Used in the simplest way, a variable is a just a name for an integer, just like
“Lassie” is just a name for your pet dog.
Begin Footnote: But as already noted in Chapter 2, a variable name is in reality
a storage cell that holds the named integer. For the moment, don’t worry about
the storage cell; this detail is revealed only when necessary in a later section. End
Footnote
To give the name, quarters, to the integer, 9, we write this Java statement:

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

Figure 3.1: change computing program


/** TotalVariables computes the amount of change I have, based on the values
* named by the four variables, quarters, dimes, nickels, and pennies. */
public class TotalVariables
{ public static void main(String[] args)
{ int quarters = 9;
int dimes = 2;
int nickels = 0;
int pennies = 6;

System.out.println("For these quantities of coins:");


System.out.print("Quarters = ");
System.out.println(quarters);
System.out.print("Dimes = ");
System.out.println(dimes);
System.out.print("Nickels = ");
System.out.println(nickels);
System.out.print("Pennies = ");
System.out.println(pennies);
System.out.print("The total is ");
System.out.println( (quarters * 25) + (dimes * 10)
+ (nickels * 5) + (pennies * 1) );
}
}

Here is an improved version of the change-counting example in the previous sec-


tion; it names each of the four quantities and prints a detailed description of the
quantities and their total. Here is what the program prints:

For these quantities of coins:


Quarters = 9
Dimes = 2
Nickels = 0
Pennies = 6
The total is 251

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,

251 / 100 computes to 2,

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, %:

251 % 100 computes to 51,

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

Figure 3.2: Calculating the value of change


/** Total computes the amount of change I have, based on
the values named by the variables,
quarters, dimes, nickels, and pennies */
public class Total
{ public static void main(String[] args)
{ int quarters = 5;
int dimes = 2;
int nickels = 0;
int pennies = 6;

System.out.println("For these quantities of coins:");


System.out.println("Quarters = " + quarters);
System.out.println("Dimes = " + dimes);
System.out.println("Nickels = " + nickels);
System.out.println("Pennies = " + pennies);
int total = (quarters * 25) + (dimes * 10) + (nickels * 5) + (pennies * 1);
System.out.println("The total is " + (total / 100) + " dollars and "
+ (total % 100) + " cents");
}
}

will print the lines

Hello!
Hello49

In Java, the + operator is overloaded to represent both numerical addition as well as


textual string concatenation (appending one string to another). Perhaps this is not
a wise use of the + symbol, but it is certainly convenient!
The above trick lets us print the dollars-cents total as one long statement:

System.out.println("The total is " + (total / 100) + " dollars and "


+ (total % 100) + " cents");

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:

For these quantities of coins:


Quarters = 4, worth 100 cents
Dimes = 0, worth 0 cents
Nickels = 1, worth 5 cents
Pennies = 1, worth 1 cents
The total is 106 cents

3. Modify Figure 2 so that it displays its output in decimal format, e.g.,

The total is $2.51

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.”)

4. For practice, write an application in Java that prints a multiplication table of


all pairs of numbers between 1 and 3, that is, 1*1, 1*2, and so on, up to 3*3.

3.2.1 Variables Can Vary: Assignments


As the examples in the previous section showed, variables name numerical quantities.
But they can do more than that—the value that a variable names can change as a
computation proceeds. Unlike the variables in algebra, computer variables can truly
vary.
Here’s why: When a variable is initialized, a storage cell in primary storage is
created, and the number named by the variable is placed into the cell. Here is a
picture of the object created in primary storage by class Total in Figure 2 just after
the four variables are initialized:
Total
main
{ int quarters == 5
int dimes == 2
int nickels == 0
int pennies == 6
> System.out.println("For these quantities of coins:");
System.out.println("Quarters = " + quarters);
...
}
64

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

1. Set the starting value of money.

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.

5. The remainder of money is printed as pennies.


Here is how we might write the first step in Java:
int dollars = 3;
int cents = 46;
int money = (dollars * 100) + cents;

The second step of the algorithm is cleverly written as follows:


System.out.println("quarters = " + (money / 25));
money = money % 25;

These statements exploit integer division, /, and integer modulo, %.


In the first statement, money / 25 calculates the maximum number of quarters to
extract from money. There are two important points:

1. The integer division operator, /, calculates integer quotient—the number of


times 25 can be subtracted from money without leaving a negative remainder. In
the example, since money’s cell holds 346, the quotient is 13, because a maximum
of 13 quarters (13*25 = 325) can be wholly subtracted from 346 without leaving
a negative remainder. Here are additional examples for intuition:

• 14 / 3 computes to 4 (and the remainder, 2, is forgotten)


• 6 / 3 computes to 2 (and there is no remainder)
• 4 / 5 computes to 0, because 5 cannot be wholly subtracted from 4

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

Figure 3.3: change-making program


/** MakeChange calculates the change for the amounts in variables
* dollars and cents. */
public class MakeChange
{ public static void main(String[] args)
{ int dollars = 3;
int cents = 46;
int money = (dollars * 100) + cents;
System.out.println("quarters = " + (money / 25));
money = money % 25;
System.out.println("dimes = " + (money / 10));
money = money % 10;
System.out.println("nickels = " + (money / 5));
money = money % 5;
System.out.println("pennies = " + money); }
}

1. By the statement,

money = money - ((money / 25) * 25);

which calculates the monetary value of the extracted quarters and subtracts
this from money.

2. By the more elegant statement,

money = money % 25;

whose modulo operator, %, calculates the integer remainder of dividing money


by 25 and assigns it to money. In this example, the remainder from performing
346/25 is of course 21. Here are additional examples of computing remainders:

• 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

int dollars; // this is a declaration of dollars; it creates a cell


dollars = 3; // this assigns 3 to the cell

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.

4. Write this sequence of statements in Java:

• A variable, my money, is initialized to 12.


• my money is reduced by 5.
• my money is doubled.
• my money is reset to 1.
• The value of my money is sent in a println message to System.out.

5. Write an execution trace of this application:

public class Exercise3


{ public static void main(String[] args)
{ int x = 12;
int y = x + 1;
x = x + y;
y = x;
System.out.println(x + " equals " + y);
}
}

3.3 Arithmetic with Fractions: Doubles


Here is the formula for converting degrees Celsius into degrees Fahrenheit:

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

(1.2 + (-2.1 / 8.4)) * 0.33


=> (1.2 + (-0.25)) * 0.33
=> (0.95) * 0.33 => 0.3135

Note that division, /, on fractional numbers produces fractional results—for example,


7.2 / 3.2 computes to 2.25, and 6.0 / 4.0 computes to 1.5.
Returning to the temperature conversion formula, we write it as a Java application.
The key statements are these two:
int c = 22;
double f = ((9.0/5.0) * c) + 32;

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 /:

• When an arithmetic operation, like +, is applied to two arguments of data type


int, e.g., 3 + 2, then the result of the operation is guaranteed to be an integer—
have data type int—as well.

• When an arithmetic operation is applied to two numerical arguments, and at


least one of the arguments has type double, e.g., 3 + 2.5, then the result is
guaranteed to have type double.

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

Figure 3.4: temperature conversion


/** CelsiusToFahrenheit converts a Celsius value to Fahrenheit. */
public class CelsiusToFahrenheit
{ public static void main(String[] args)
{ int c = 22; // the degrees Celsisus
double f = ((9.0/5.0) * c) + 32;
System.out.println("For Celsius degrees " + c + ",");
System.out.println("Degrees Fahrenheit = " + f);
}
}

For Celsius degrees 22,


Degrees Fahrenheit = 71.6

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

1. Rewrite the program, CelsiusToFahrenheit, into a program, FahrenheitToCelsius,


that converts a double value of Fahrenheit degrees into double value of Celsius
degrees and prints both values.

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. Calculate the answers to these expressions; be careful about the conversions


(casts) of integer values to doubles during the calculations:

(a) (5.3 + 7) / 2.0


(b) (5.3 + 7) / 2
(c) 5.3 + (7 / 2)
(d) (1.0 + 2) + ((3%4)/5.0)
72

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

It is perfectly acceptable to save a boolean answer in a variable:

boolean b = (3 < 2);

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;

Finally, the usual numerical operations (+, -, *, / , %) cannot be used with


boolean arguments, because it makes no sense to “add” or “subtract” logical truths.
(E.g., what would true - false mean?) The Java compiler will report an error
message if you attempt such actions.

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.

2. Build a Java application that is told a quantity of dimes and a quantity of


nickels and prints whether the quantity of dimes is worth less than the quantity
of nickels. Test the application with 4 dimes and 6 nickels.

3. Calculate the results of these expressions:

(a) (3 * 2) >= (-9 - 1)


(b) 3 * 2 != 5.5 + 0.4 + 0.1
74

3.5 Operator Precedences


When the Java interpreter calculates an arithmetic expression, it proceeds from left
to right: Given an expression of the form, EXPRESSION1 operator EXPRESSION2, the
interpreter evaluates EXPRESSION1 to its result before it evaluates EXPRESSION2 and
then applies the operator to the two resulting values.
Parentheses within an expression are highly recommended to make clear which
expressions belong as operands to which operators. Consider 1 * 2 + 3–what is the
* operator’s second operand? Is it 2? Or 2 + 3? If the expression was properly
parenthesized, as either 1 * (2 + 3) or (1 * 2) + 3, this confusion would not arise.
If parentheses are omitted from an expression that contains multiple operators,
the Java compiler will apply its own rules to direct the computation of the Java
interpreter. These rules, called operator precedences, are listed shortly.
One simple and “safe” example where parentheses can be omitted is a sequence
of additions or multiplications, e.g., 1 + 2 + 3 or 1 * 2 * 3. There is no confusion
here, because both addition and multiplication are associative—the order that the
additions (multiplications) are performed does not affect the result.
When multiplications and divisions are mixed with additions and subtractions in
an expression without parentheses, the interpreter still works left to right, but when
there is a choice, the interpreter does multiplications and divisions before additions
and subtractions. We say that the precedences of multiplication and division are
higher than those of addition and subtraction. For example, 1 + 2.0 * 3 results in
7.0, because there is a choice between computing 1 + 2.0 first or 2.0 * 3 first, and
the Java interpreter chooses the latter, that is, the multiplication is performed just
as if the expression was bracketed as 1 + (2.0 * 3).
Here is the ordering of the precedences of the arithmetic operations, in the order
from highest precedence (executed first, whenever possible) to lowest (executed last):

unary negation, e.g., -3


multiplication, *, division/quotient, /, and modulo %
addition/string concatenation, +, and subtraction, -
comparison operations, <, <=, >, >=
comparisons of equality, ==, and inequality, !=

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

2. Another way of understanding operator precedences is that the precedences are


rules for inserting parentheses into expressions. For example, the precedences
tell us that the expression 1 + 2.0 * 3 should be bracketed as 1 + (2.0 * 3).
Similarly, 5 - 3 / 2 * 4 + 6 * -2 / -3 + 1.5 is bracketed as ((5 - ((3 / 2)
* 4)) + ((6 * -2) / -3)) + 1.5. For each of the expressions in the previous
exercise, write the expression with its brackets.

3.6 Strings, Characters, and their Operations


In Chapter 2 we noted that a textual phrase enclosed in double quotes, e.g., "hello",
is a string. Like numbers and booleans, strings are values that can be computed
upon and saved in variables. Java’s data-type name for strings is String (note the
upper-case S); here is an example:
String name = "Fred Mertz";

Literal strings can contain nonletters, such as blanks, numerals, tabs, and backspaces;
here are examples:

• " " (all blanks)

• "1+2" (two numerals and a plus symbol—not an addition!)

• "" (an empty string)

• "!?,.\t\b\’\"\n\r\\" (four punctuation symbols, followed by

– 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\\");

you will see

!?,. ’"
\

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:

System.out.println("My name is " + name);

The operator can also force a number (or boolean) to be concatenated to a string,
which is a useful convenience for simply printing information:

System.out.println("My name is R2D" + (5 - 3));

Of course, the parenthesized subtraction is performed before the integer is attached


to the string.
Integers and doubles are different from strings—you can multiply and divide with
integers like 49 and 7 but you cannot do arithmetic on the the strings "49" and "7".
Indeed, "49" + "7" computes to "497", because the + symbol represents concatenation
when a string argument is used.
Later in this Chapter, we will learn techniques that transform numbers into strings
and strings into numbers; these techniques will be necessary for an application to
accept input data from a human user and do numerical computation on it.
The String type owns a rich family of operations, and we present several of them
here. The operations for strings have syntax different from the arithmetic and com-
parison operators previously seen because strings are actually objects in Java, and the
operations for strings are actually methods. For this reason, the following examples
should be carefully studied:

• 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

For example, "hello".equals("hel"+"lo") returns the boolean answer, true,


whereas "hello".equals("Hello") results in false.
Of course, the method can be used with a string that is named by a variable:

String s = "hello";
System.out.println(s.equals("hel"+"lo"));
System.out.println(s.equals(s));

prints true on both occasions.

• To determine a string’s length, use the length() method: for string S, the
message

S.length()

returns the integer length of the string, e.g.,

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,

String s = " ab c ";


String t = s.trim();

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

Figure 3.5: methods on strings

Method Semantics Data typing restrictions


S1.equals(S2) equality comparison—returns S1 and S2 must have type
whether strings S1 and S2 hold String; the answer has type
the same sequence of characters boolean.
S.length() returns the length of string S if S has data type String,
then the result has type int
S.charAt(E) returns the character at position if S has data type String and
E in S E has data type int, then the
result has data type char
S.substring(E1, returns the substring starting at if S has data type String, E1
E2) position E1 and extending to po- and E2 have data type int,
sition E2 - 1 then the result is a String
S.toUpperCase() returns a string that looks like S if S has data type String, the
but in upper-case letters only result has data type String
S.toLowerCase() returns a string that looks like S if S has data type String, the
but in lower-case letters only result has data type String
S.trim() returns a string like S but with- if S has data type String, the
out leading or trailing blanks result has data type String
S1.indexOf(S2, searches S1 for the first occur- if S1 and S2 have data type
i) rence of S2 that appears inside String and i has data type
it, starting from index i within int, the result has data type
S1. If S2 is found at position j, int
and j >= i, then j is returned;
otherwise, -1 is returned.
S1.compareTo(S2) like the equals method, if S1 and S2 have data type
compareTo compares the char- String, then the result has
acters in string S1 to S2: if S1 data type int
is “less than” S2 according to
the lexicographic (dictionary)
ordering, -1 is returned; if S1
and S2 are the same string, 0 is
returned; if S1 is “greater than”
S2 according to the lexicographic
ordering, then 1 is returned.
3.6. STRINGS, CHARACTERS, AND THEIR OPERATIONS 79

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:

boolean result = s.charAt(0) == ’a’;

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) 2 + ("a" + " ") + "bc"


(b) 1 + "" + 2 + 3
(c) 1 + 2 + "" + 3
(d) 1 + 2 + 3 + ""
(e) 1 + "" + (2 + 3)

2. Here are two strings:

String t = "abc ";


String u = "ab";

What is printed by these statements?

(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());

3.7 Data-Type Checking


Because of the variety of values—integers, doubles, booleans, and strings—that can be
expressed in a Java program, the Java compiler must enforce consist use of these values
in statements; this is called data-type checking. For example, the second statement
below is incorrect,
3.7. DATA-TYPE CHECKING 81

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.

• They help enfore compatibilities of expressions to variables in assignments, e.g.,


d = 1 * 2.3 is an acceptable assignment, because it places a double into a cell
declared to hold only doubles.
We can list the steps the Java compiler takes to type-check an expression; consider
these two variables and the arithmetic expression that uses them:
int i = 1;
double d = 2.5;
... ((-3 * i) + d) > 6.1 ...

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

Figure 3.6: data-type tree for an arithmetic expression

(( -3 * i )+ d ) > 2

int int int

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

• reference (object) types, such as GregorianCalendar


Variables can be declared with either form of data type.
Surprisingly, type String is a reference type in Java, which means that strings are
in fact objects! This is the reason why the string operations in Table 5 are written
as if they were messages sent to objects—they are!

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);

3.8 Input via Program Arguments


The applications in this chapter are a bit unsatisfactory, because each time we want
to reuse a program, we must reedit it and change the values in the program’s variable
initialization statements. For example, if we wish to convert several temperatures
from Celsius to Fahrenheit, then for each temperature conversion, we must reedit the
program in Figure 4, recompile it, and restart it.
It would be far better to write the temperature conversion application just once,
compile it just once, and let its user start the application again and again with
different numbers for degrees Celsius. To achieve this, we make the application read
a numeric temperature as input data each time it starts.
Java uses program arguments (also known as command-line arguments) as one
simple way to supply input data to an application.
Begin Footnote: In the next Chapter, we learn a more convenient method for
supplying input data. End Footnote
When she starts an application, the user types the program arguments—these
are the inputs. The application grabs the program arguments and assigns them to
internal variables.
Say that we have rewritten the temperature conversion program in Figure 4 to
use program arguments:
• If you installed the JDK, then when you start an application, you type the
program argument on the same line as the startup command, that is, you type

java CelsiusToFahrenheit 20

if you want to convert 20 Celsius to Fahrenheit.


84

• 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

Figure 3.7: Supplying a program argument to an IDE


86

3.8.1 Converting between Strings and Numbers and Format-


ting
With the aid of a “helper object,” we can convert a string of numerals, like "20", into
the integer, 20. And, we can use another object to convert an integer or double into
a nicely formatted string for printing.

Converting Strings into Integers and Doubles


The Java designers have written class Integer, from which we can construct an
object that knows how to translate a string of numerals into an integer. (At this
point, you should review the example in Figures 3 and 4, Chapter 2, that used class
GregorianCalendar to create a similar “helper” object.)
Say that S is a string of numerals that we wish to translate into the corresponding
integer, e.g.,
String S = "20";

Here are the steps we take:


• Create a new Integer object, by saying

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.,

Integer convertor = new Integer(S);


int c = convertor.intValue();

or in just one:

int c = new Integer(S).intValue();

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

Converting a Number into a String and Formatting it for Printing


Converting a number into a string is simple: Because the operator, +, will combine a
number and a string, we can concatenate an empty string to a number to “convert”
it to a string:
double d = 68.8;
String s = d + "";
In the above example, s is assigned "68.8".
When printing a double, we often wish to present it in a fixed decimal format.
For example,
System.out.println ("$" + (100.0/3.0));
prints
$33.333333333333336
where we probably would prefer just $33.33 to appear.
There is yet another class, named DecimalFormat, that constructs objects that
know how to format doubles in a fixed decimal format:
• Because the class is located in the package, java.text, include

import java.text.*;

at the beginning of the application.


• Construct a DecimalFormat object as follows:

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

• Send a format(D) message to the object, where D is a double value, e.g.,

DecimalFormat formatter = new DecimalFormat("0.00");


double d = 100.0 / 3.0;
String s = formatter.format(d);

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.

Here are the steps presented together:

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));
...
}
}

3.8.2 Temperature Conversion with Input


We use the techniques from the previous section to make the temperature conversion
application from Figure 4 read a Celsuis temperature as its input and display its
translation to Fahrenheit. The input temperature will be a program argument that
is supplied at the startup command, e.g.,

java CelsiusToFahrenheit 20

Within the application, the argument is fetched and converted to an integer by writ-
ing:

int c = new Integer(args[0]).intValue();

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:

double f = ((9.0/5.0) * c) + 32;


DecimalFormat formatter = new DecimalFormat("0.0");
System.out.println("Degrees Fahrenheit = " + formatter.format(f));
3.8. INPUT VIA PROGRAM ARGUMENTS 89

Figure 3.8: application using a program argument


import java.text.*;
/** CelsiusToFahrenheit converts an input Celsius value to Fahrenheit.
* input: the degrees Celsius, a program argument, an integer
* output: the degrees Fahrenheit, a double */
public class CelsiusToFahrenheit
{ public static void main(String[] args)
{ int c = new Integer(args[0]).intValue(); // args[0] is the program argument
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));
}
}

Figure 8 shows the revised temperature-conversion program.


The program in Figure 8 can be used as often as we like, to convert as many
temperatures as we like:
java CelsiusToFahrenheit 20

java CelsiusToFahrenheit 22

java CelsiusToFahrenheit -10

and so on—try it.

An Execution Trace of Temperature Conversion


The concepts related to program arguments are important enough that we must
analyze them in detail with an execution trace. Say that the user starts the application
in Figure 8 with the program argument, 20, which the Java interpreter reads as the
string, "20".
A CelsiusToFahrenheit object is created in computer storage, and its execution
starts at the first statement of its main method:
CelsiusToFahrenheit
main(args[0] == "20" )
{ >int c = new Integer(args[0]).intValue();
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));
}
90

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;
...
}

Next, the helper object is created in computer storage:


CelsiusToFahrenheit a1 : Integer
main(args[0] == "20" ) holds 20
...
{ int c == ? intValue()
{ a method that returns 20 }
> c = a1.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

Object a1 quickly returns 20 to the client object:


CelsiusToFahrenheit
main(args[0] == "20" )
{ int c == ?
> c = 20
double f = ((9.0/5.0)*c) + 32;
...
}

(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

3. Revise either or both of the programs, CelsiusToFahrenheit and KilometersToMiles,


that you wrote as solutions to the preceding Exercises set, “Arithmetic with
Fractions: Doubles,” so that the programs use program arguments.

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

5. Write a sequence of three statements that do the following: reads a program


argument as an integer, i; reads a program argument as an integer, j; prints
true or false depending on whether i’s value i s greater than j’s.

6. Exercise 3 of the section, “Named Quantities: Variables,” had difficulties print-


ing correctly formatted answers when the dollars-cents amount printed con-
tained a cents amount less than 10. Use class DecimalFormat to solve this
problem. Test your solution with the inputs of four quarters, one nickel and
one penny.

3.9 Diagnosing Errors in Expressions and Vari-


ables
Expressions and variables give a programmer more power, but this means there is
more opportunity to generate errors. The errors can have these two forms:

• 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 );

The compiler notices this and announces,

Test.java:4: ’)’ expected.


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:

Test1.java:5: Class string not found in type declaration.


string s;
^
3.9. DIAGNOSING ERRORS IN EXPRESSIONS AND VARIABLES 93

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);

generates this compiler complaint,


Test.java:4: Incompatible type for +. Can’t convert boolean to int.
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);

If allowed to execute, these statements would cause disaster, because i holds no


number to multiply by 2. The Java compiler notices this and complains.
Yet another variable-related error is the declaration of one name in two declara-
tions:
int a = 1;
double a = 2.5;
System.out.println(a); // which a?

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:

int i = new Integer(args[0]).intValue();


System.out.println(1 / i);

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.10 Java Keywords and Identifiers


At the beginning of this chapter we noted that a variable name is an identifier.
Stated precisely, an identifier is a sequence of letters (upper and lower case), digits,
underscore symbol, , and dollar sign, $, such that the first character is nonnumeric.
Thus, x 1 is an identifier, but 1 x is not.
Identifiers are used to name variables, of course, but they are used for naming
classes and methods also, e.g., Total and println are also identifiers and must be
spelled appropriately. For consistent style, classes are named by identifiers that begin
with an upper-case letter, whereas method names and variable names are named by
identifiers that begin with lower-case letters.
Java’s keywords have special meaning to the Java compiler and cannot be used
as identifiers. Here is the list of keywords: abstract boolean break byte case cast
catch char class const continue default do double else extends final finally
float for future generic goto if implements import inner instanceof int interface
long native new null operator outer package private protected public rest return
short static super switch synchronized this throw throws transient try var void
volatile while

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;

• arithmetic operators (from Figure 1):

System.out.println( (quarters * 25) + (dimes * 10)


+ (nickels * 5) + (pennies * 1) );

• assignment statement (from Figure 2):

money = money % 25;

• data types (from Figure 4):

int c = 22;
double f = ((9.0/5.0) * c) + 32;
96

• program argument (from Figure 6):

int c = new Integer(args[0]).intValue();

New Terminology
• operator: a symbol, like * or -, that denotes an arithmetic operation

• operand: an argument to an operator, e.g., i in 3 + i.

• 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.

• referencing a variable: fetching a copy of the value saved in a variable

• declaration: the statement form that constructs a variable

• initialization: the statement that gives a variable its first, initial value.

• assignment: the statement form that inserts a new value into a variable

• program argument: input data that is supplied to a Java application at startup,


typically by typing it with the startup command.

• compile-time error: an error that is identified by the Java compiler

• run-time error (exception): an error that is identified by the Java interpreter,


that is, when the application is executing.

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.

• Input data that is supplied by means of program arguments are accepted as


strings and are saved in the variables, args[0], args[1], and so on, in the main
method.

• Strings can be converted into numbers, if needed, by means of objects created


from class Integer and class Double.
3.12. PROGRAMMING PROJECTS 97

New Classes for Later Use


• class Integer. Converts strings of numerals into integers; has method intValue(),
which returns the integer value of the string argument, S, in new Integer(S).
See Figure 6.

• 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).

• class DecimalFormat. Found in package java.text. Has the method, format(D),


which returns the decimal-formatted value of double, D, based on the pattern,
P, used in new DecimalFormat(P). See Figure 8.

3.12 Programming Projects


1. One ounce equals 28.35 grams. Use this equivalence to write an application
that converts a value in ounces to one in grams; an application the converts a
value in kilograms to one in pounds. (Recall that one kilogram is 1000 grams,
and one pound is 16 ounces.)

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

4. The two roots of the quadratic equation ax2 + bx + c = 0 (where a, x, b, and c


are doubles) are defined
√ √
−b + b2 − 4ac −b − b2 − 4ac
x= and x =
2a 2a
Write an application that computes these roots from program arguments for a,
b, and c. (Hint: the square root of E is coded in Java as Math.sqrt(E).)

5. (a) The compounded total of a principal, p (double), at annual interest rate,


i (double), for n (int) years is

total = p((1 + i))n )

Write an application computes the compounded total based on program


arguments for p, i, and n. (Hint: the power expression, ab , is coded in
Java as Math.pow(a,b).)
(b) Rewrite the above equation so that it solves for the value of p, thereby
calculating the amount of starting principal needed to reach total in n
years at annual interest rate i. Write an application to calculate this
amount.
(c) Rewrite the above equation so that it solves for the value of i, thereby
calculating the interest rate (the so-called discount rate) needed to reach
a desired total starting at p and taking y years. Write an application to
calculate this interest rate.

6. (a) The annual payment on a loan of principal, p (double), at annual interest


rate, i (double), for a loan of duration of y (int) years is

(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 )

(Again, the measures of values must be consistent: If velocity is stated in terms


of miles per hour, then time must be stated in hours, and distance will be
computed in miles.) Use this formula to write the following applications:
100

(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, ...

10. Write an application, CountTheSeconds, that converts an elapsed time in seconds


into the total number of days, hours, minutes, and leftover seconds contained in
the time. For example, when the user submits an input of 289932, the program
replies

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:

For input 12345,


the sum of its digits is 15.
Divisible by 9? false

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

14. Next, use Math.random() to modify the CelsiusToFahrenheit application in Fig-


ure 4 so that it each time it is started, it chooses a random Celsius temperature
between 0 and 40 and prints its conversion, e.g.,
Did you know that 33 degrees Celsuis
is 91.4 degrees Fahrenheit?

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

The program replies


2 divides 28? true
3 divides 28? false
5 divides 28? false
7 divides 28? true

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

3.13 Beyond the Basics


3.13.1 Longs, Bytes, and Floats

3.13.2 Helper Methods for Mathematics

3.13.3 Syntax and Semantics of Expressions and Variables

In these optional, supplemental sections, we examine topics that extend the essential
knowledge one needs to program arithmetic.

3.13.1 Longs, Bytes, and Floats


Almost all the computations in this text rely on just integers, doubles, booleans, and
strings. But the Java language possesses several other primitive data types, and we
survey them here.
The data type int includes only those whole numbers that can be binary coded in
a single storage word of 32 bits—that is, numbers that fall in the range of -2 billion
to 2 billion (more precisely, between −231 and (231 ) − 1). To work with integers that
fall outside this range, we must use long integers. (The data type is long.) Create
a long integer by writing a number followed by an L, e.g., 3000000000L is a long 3
billion. Long variables are declared in the standard way, e.g.,
long x = 3000000000L;

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

Figure 3.9: data types

Data Type name Members Sample values Sample expression


byte one-byte whole (byte)3 (byte)3 +
numbers, in range (byte)-12 (byte)1
-128 to 127
int one-word integers 3, -2000, 0 3+(2-1)
(whole numbers),
in range -2 billion
to 2 billion
long two-word integers 3000000000L, 3L 2000000000L +
(whole numbers) 1000000000L
float fractional numbers 6.1F, 3.0E5F, (float)(10.0/3.0F)
with 8 digits of pre-
cision
double fractional numbers 6.1, 3.0E5, 10.5 / -3.0
with 16 digits of -1e-1
precision
char single characters ’a’, ’\n’, ’2’ ’a’ + 1
(two-byte integers)
boolean truth values true, false (6 + 2) > 5
String text strings "Hello to you!", "Hello " + "to"
"", "49"

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;

which saves .0003F in x.


Table 9 summarizes the data types we have encountered.

3.13.2 Helper Methods for Mathematics


Within the java.lang package, class Math provides a variety of methods to help with
standard mathematical calculations. (Some of the methods were mentioned in the
Programming Projects section.) Table 10 lists the more commonly used ones; many
more can be found in the Java API for class Math.
104

Figure 3.10: mathematical methods

Constant name Semantics Data typing


Math.PI the value of PI, 3.14159... double
Math.E the value of E, 2.71828... double
Method Semantics Data typing
Math.abs(E) returns the absolute value of its nu- returns the same data type
meric argument, E as its argument, e.g., a
double argument yields a
double result
Math.ceil(E) returns the nearest integer value that E should be a double; the
is not less than E. (That is, if E has a result is a double
non-zero fraction, then E is increased
upwards to the nearest integer.)
Math.floor(E) returns the nearest integer value that E should be a double; the
is not greater than E. (That is, if E has result is a double
a non-zero fraction, then E is decreased
downwards to the nearest integer.)
Math.cos(E) returns the cosine of its argument, E, a returns a double
numeric value in radians. (Note: 180
degrees equals PI radians.)
Math.max(E1, returns the maximum of its two nu- Similar to multiplication—
E2) meric arguments the result data type de-
pends on the types of the
arguments
Math.min(E1, returns the minimum of its two nu- Similar to multiplication—
E2) meric arguments the result data type de-
pends on the types of the
arguments
Math.pow(E1, returns E1 raised to the power E2: Both arguments must be
E2) E1E2 numeric type; the result is
a double
Math.random() returns a (pseudo)random number the result has type double
that is 0.0 or greater but less than 1.0
Math.round(E) returns E rounded to the nearest whole if E has type float, the re-
number sult has type int; if E has
type double, the result has
type long
Math.sin(E) returns the sine of its argument, E, a returns a double
numeric value in radians. (Note: 180
degrees equals PI radians.)
Math.sqrt(E) returns the square root of its argument The arguments must be
numeric; the result is a
double
Math.tan(E) returns the tangent of its argument, E, returns a double
a numeric value in radians. (Note: 180
degrees equals PI radians.)
3.13. BEYOND THE BASICS 105

3.13.3 Syntax and Semantics of Expressions and Variables


The syntax and semantics of expressions and variables are surprisingly rich, and we
will reap rewards from investing some time in study. The starting point is the notion
of “data type”:

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).

Statement and Assignment


The addition of declarations and assignments motivates these additions to the syntax
rule for statements:
STATEMENT ::= DECLARATION
| STATEMENT_EXPRESSION ;
STATEMENT_EXPRESSION ::= OBJECT_CONSTRUCTION | INVOCATION | ASSIGNMENT

The syntax of an assignment statement goes


106

ASSIGNMENT := VARIABLE = EXPRESSION


VARIABLE ::= IDENTIFIER

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;

3. store the value into the cell.

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

A LITERAL is a constant-valued expression, like 49, true, or "abc". The ( TYPE )


EXPRESSION is a cast expression, e.g., (int)3.5.
Semantics: Evaluation of an expression proceeds from left to right, following the
operator precedences set down in the section, “Operator Precedences.”
Chapter 4

Input, Output, and State


4.1 Interactive Input

4.1.1 Dialog Output

4.2 Graphical Output

4.2.1 Panels and their Frames

4.2.2 Customizing Panels with Inheritance

4.3 Format and Methods for Painting

4.3.1 Constructor Methods and this object

4.4 Objects with State: Field Variables

4.4.1 Using Fields to Remember Inputs and Answers

4.4.2 Scope of Variables and Fields

4.5 Testing a Program that Uses Input

4.6 Summary

4.7 Programming Projects

4.8 Beyond the Basics

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

Figure 4.1: an architecture with an input-view

Controller [the “start-up” component]


main

Input View Output View


[reads interactive input] [displays output]

4.1 Interactive Input

The temperature-conversion program in Figure 8, Chapter 3, receives its input from


a program argument, which is data that is supplied the moment that a program is
started. (Review the section, “Input via Program Arguments,” in that chapter for the
details.) The program-argument technique is simple, but it is not realistic to demand
that all input data be supplied to an application the moment it is started—indeed,
most programs use interactive input, where the program lets its user submit data
while the program executes.

To implement interactive input, an application must use an input-view component,


as displayed in Figure 1.

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

displays an input dialog that requests the input Celsius temperature:

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:

For Celsius degrees 20,


Degrees Fahrenheit = 68.0

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

1. At the beginning of the application, add the statement,

import javax.swing.*;

so that the Java interpreter is told to search in the javax.swing package for
JOptionPane.

2. Create the dialog on the display by stating

String input = JOptionPane.showInputDialog(PROMPT);


110

Figure 4.2: Interactive input from a dialog


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 */
public class CelsiusToFahrenheit2
{ public static void main(String[] args)
{ String input =
JOptionPane.showInputDialog("Type an integer Celsius temperature:");
int c = new Integer(input).intValue(); // convert input into an int
double f = ((9.0 / 5.0) * c) + 32;
DecimalFormat formatter = new DecimalFormat("0.0");
System.out.println("For Celsius degrees " + c + ",");
System.out.println("Degrees Fahrenheit = " + formatter.format(f));
}
}

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

Figure 4.3: architecture for temperature-conversion application

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.

4.1.1 Dialog Output


If an application’s output consists of only one line of text, we might present this line
in an output dialog, rather than in the command window. Indeed, the output from
the previous application might be presented in an output dialog like this:

When the user presses the OK button, the dialog disappears.


The JOptionPane class contains a method, showMessageDialog, which constructs
and displays such dialogs. The format of messages to showMessageDialog goes
JOptionPane.showMessageDialog(null, MESSAGE);
where MESSAGE is the string to be displayed in the dialog. The first argument, null,
is required for reasons explained in Chapter 10; for now, insert null on faith.
We can modify Figure 2 to use the dialog—use this statement,
4.2. GRAPHICAL OUTPUT 113

Figure 4.4: a simple graphics window

JOptionPane.showMessageDialog(null, c + " Celsius is "


+ formatter.format(f) + " Fahrenheit");

as a replacement for the two System.out.println statements at the end of the main
method in Figure 2.

4.2 Graphical Output


We now build output-views that replace System.out in our applications. The views
we build will be “graphics windows” that display words, shapes, and colors on the
display. Graphics windows are objects, and like all objects they must be constructed
from classes. In the Java libraries, there already exist classes from which we can
construct empty windows. To build an interesting window, we use the prewritten
classes in a new way—we extend them by inheritance.
A simple graphics window appears in Figure 4. It takes tremendous work to
build the window in the Figure from scratch, so we start with the classes of windows
available in the javax.swing library and extend them to create windows like the one
in the Figure. To get started, we study a specific form of graphics window called a
frame and we learn how to “paint” text and diagrams on a panel and insert the panel
into its frame.
114

Figure 4.5: creating an invisible frame


import javax.swing.*;
/** FrameTest1 creates a frame */
public class FrameTest1
{ public static void main(String[] args)
{ JFrame sample frame = new JFrame();
System.out.println("Where is the frame?");
}
}

Figure 4.6: creating an empty frame


import javax.swing.*;
/** FrameTest2 creates and displays an empty frame */
public class FrameTest2
{ public static void main(String[] args)
{ JFrame sample frame = new JFrame();
sample frame.setSize(300, 200); // tell frame to size itself
sample frame.setVisible(true); // tell frame to make itself visible
System.out.println("Frame has appeared!");
}
}

4.2.1 Panels and their Frames


In Java, graphics text and drawings are displayed within a frame. Frames are built
from class JFrame, which lives in the package, javax.swing. Any application can use
class JFrame; see Figure 5.
The frame is created by the phrase, new JFrame(), and we name the frame,
sample frame. As usual, the appropriate import statement must be included.
If you execute this example, you will be disappointed—no frame appears, and
the application itself does not terminate, even though all of main’s statements have
successfully executed. (As noted in the previous section, you must terminate the
application manually.)
We can repair the situation by sending messages to the frame object, telling it to
size itself and show itself on the display. Figure 6 shows how, by sending the messages
setSize and setVisible to the JFrame object.
The message, setSize(300, 200), tells sample frame to size itself with width 300
pixels and height 200 pixels. (The unit of measurement, “pixels,” is explained later.
The sizes, 300 and 200, were chosen because frames in 3-by-2 proportion tend to be
4.2. GRAPHICAL OUTPUT 115

Figure 4.7: an empty frame

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.

4.2.2 Customizing Panels with Inheritance


A panel is the Java object on which one draws or “paints.” The javax.swing library
contains a class JPanel from which we can construct blank panels. To draw or paint
on the panel, we write instructions for painting and attach them to the panel using
a technique called inheritance. We introduce panel-painting-by-inheritance with a
simple example.
Say that the graphics window in Figure 7 should display the text, Hello to you!,
in red letters, giving the results in Figure 8. To do this, we take these steps:

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.

2. We construct an object from class TestPanel, we construct a frame object


from class JFrame, and we insert the panel object into the frame object.

3. We size and show the frame, like we did in Figure 6, and the frame with its
panel appears on the display.
116

Figure 4.8: a graphics window with text

Here is the format we use to write class TestPanel:

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
}
}

The crucial, new concepts are as follows:

• public class TestPanel asserts that we can construct TestPanel objects from
the new class; we do it by stating, new TestPanel().

• The phrase, extends JPanel, connects class TestPanel to class JPanel—it


asserts that every TestPanel object that is constructed will have the contents of
a prebuilt, blank JPanel object plus whatever extra instructions we have written
within class TestPanel.
Here is some terminology: TestPanel is called the subclass; JPanel is the super-
class.
When we write class TestPanel, we do not edit or otherwise alter the file where
class JPanel resides.
4.2. GRAPHICAL OUTPUT 117

Figure 4.9: a panel that displays a string


import java.awt.*;
import javax.swing.*;
/** TestPanel creates a panel that displays a string */
public class TestPanel extends JPanel
{ /** paintComponent paints the panel
* @param g - the ‘‘graphics pen’’ that draws the items on the panel */
public void paintComponent(Graphics g)
{ g.setColor(Color.red);
g.drawString("Hello to you!", 30, 80);
}
}

• 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.

Figure 9 shows the completed class, TestPanel. Within paintComponent, we see


the statements,

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

Figure 4.10: Constructing a panel and inserting it into a frame


import javax.swing.*;
/** FrameTest2 creates and displays an empty frame */
public class FrameTest2
{ public static void main(String[] args)
{ // construct the panel and frame:
TestPanel sample panel = new TestPanel();
JFrame sample frame = new JFrame();
// insert the panel into the frame:
sample frame.getContentPane().add(sample panel);
// finish by sizing and showing the frame with its panel:
sample frame.setSize(300, 200);
sample frame.setVisible(true);
System.out.println("Frame has appeared!");
}
}

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:

/** paintComponent paints the window


* @param g - the ‘‘graphics pen’’ that draws the items onto the window */

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

Figure 4.11: architecture of application with MyOwnFrame

FrameTest2 MyOwnFrame
main paintComponent

JFrame JPanel Graphics


getContentPane setColor
setSize drawString
setVisible

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,

g.drawRect(left_edge, top, width, height);

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:

1. The position of the rectangle’s left edge; here, it is left edge.


2. The position of the rectangle’s top edge; here, it is top.
3. The width of the rectangle to be drawn; here, this is width.
4. The height of the rectangle to be drawn; here, this is height.

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,

g.fillRect(0, 0, frame_width, frame_height);

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,

g.fillOval(left_edge + width - diameter, top, diameter, diameter);

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

Figure 4.12: a colorful graphics window


import java.awt.*;
import javax.swing.*;
/** MyPanel creates a colorful panel */
public class MyPanel extends JPanel
{ /** paintComponent fills the panelwith the items to be displayed
* @param g - the ‘‘graphics pen’’ that draws the items */
public void paintComponent(Graphics g)
{ int frame width = 300;
int frame height = 200;
g.setColor(Color.white); // paint the white background:
g.fillRect(0, 0, frame width, frame height);
g.setColor(Color.red);
int left edge = 105; // the left border where the shapes appear
int top = 70; // the top where the shapes appear
// draw a rectangle:
int width = 90;
int height = 60;
g.drawRect(left edge, top, width, height);
// draw a filled circle:
int diameter = 40;
g.fillOval(left edge + width - diameter, top, diameter, diameter);
}
}

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.)

2. The size of the panel is 400 by 400 pixels.

3. The background color of the window is yellow. (Hint: use Color.yellow.)

4. The oval is painted with size 80 by 40.

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.)

4.3 Format and Methods for Painting


Every panel and frame, and window in general, has a width and a height. In Figure
12, the statement, setSize(300,200), sets the width of the window to 300 pixels and
its height to 200 pixels. A pixel is one of the thousands of “dots” you see on the
display screen; it holds one “drop” of color. (For example, to display the letter, “o”
on the display, the operating system must color a circle of pixels with black color.)
Within a window, each pixel has a “location,” which is specified by the pixel’s
distance from the window’s left border and its distance from the window’s top (and
4.3. FORMAT AND METHODS FOR PAINTING 123

Figure 4.13: locating a pixel at coordinates 100,50

50

100 height
200

width 300

Figure 4.14: ¡tt¿g.drawRect(105, 70, 90, 60)¡/tt¿

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

Figure 4.15: g.fillOval(105, 90, 40, 40)

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.

4.3.1 Constructor Methods and this object

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

Figure 4.16: Graphics pen methods


Methods from class Graphics

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

program that displays the current time as a clock in a window:

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
}

/** paintComponent draws the clock with the correct time.


* @param g - the graphics pen that does the drawing */
public void paintComponent(Graphics g)
{ ... }
}
Remember that public ClockWriter() contains the start-up instructions that execute
exactly once, when the ClockWriter object is first constructed by means of the phrase,
4.3. FORMAT AND METHODS FOR PAINTING 127

new ClockWriter()

Now, we understand that the semantics of the phrase is twofold:


• A ClockWriter object is constructed in primary storage;

• 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.

2. clocks frame.getContentPane().add(this) sends a message to the frame, telling


it to add this ClockWriter panel into it.
The pronoun, this, is used within ClockWriter’s constructor method to refer to
this newly built ClockWriter object whose start-up instructions are executing.
When an object refers to itself, it uses this, just like you use “I” and “me”
when you refer to yourself.

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)

hours_angle = 90 - (hours * 30)

(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

Figure 4.17: class to paint the clock


import java.awt.*;
import javax.swing.*;
import java.util.*;
/** ClockWriter draws a clock in a panel. */
public class ClockWriter extends JPanel
{ public ClockWriter()
{ int width = 200; // the width of the clock
// construct this panel’s frame:
JFrame clocks frame = new JFrame();
// and insert this panel into its frame:
clocks frame.getContentPane().add(this);
// show the frame:
clocks frame.setTitle("Clock");
clocks frame.setSize(width, width);
clocks frame.setVisible(true);
}

/** paintComponent draws the clock with the correct time.


* @param g - the graphics pen that does the drawing */
public void paintComponent(Graphics g)
{ int width = 200; // the width of the clock
g.setColor(Color.white);
g.fillRect(0, 0, width, width); // paint the background
GregorianCalendar time = new GregorianCalendar();
int minutes angle = 90 - (time.get(Calendar.MINUTE) * 6);
int hours angle = 90 - (time.get(Calendar.HOUR) * 30);
// draw the clock as a black circle:
int left edge = 50;
int top = 50;
int diameter = 100;
g.setColor(Color.black);
g.drawOval(left edge, top, diameter, diameter);
// draw the minutes’ hand red, 10 pixels smaller, with a width of 5 degrees:
g.setColor(Color.red);
g.fillArc(left edge + 5, top + 5, diameter - 10, diameter - 10,
minutes angle, 5);
// draw the hours’ hand blue, 50 pixels smaller, with a width of -8 degrees:
g.setColor(Color.blue);
g.fillArc(left edge + 25, top + 25, diameter - 50, diameter - 50,
hours angle, -8);
}

/** 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:

public static void main(String[] args)


{ new ClockWriter(); }

There is no need to attach a variable name to the ClockWriter object, so it suffices


to state merely new ClockWriter().
It seems silly to write a separate controller class that contains just these two lines of
text, so we use a standard shortcut—insert the main method into class ClockWriter
as well! At the bottom of Figure 17, you can see the method. You start the main
method in the usual way, say, by typing at the command line,

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:

hours_angle = 90 - ((hours + (minutes/60.0)) * 30)

3. Make the clock “digital” as well as “analog” by printing the correct time, in
digits, at the bottom of the window.

4.4 Objects with State: Field Variables


In Chapter 1, we noted that an object has an “identity,” it knows some things, and
it is able to do some things. In the Java language, an object’s identity is its address
in primary storage, and its abilities are its methods.
To give an object knowledge, we equip it with variables that can be shared, read,
and updated by the object’s methods. Such variables are called field variables, or
fields for short.
Here is a standard use of fields: Perhaps you noticed in the previous two examples
(MyWriter and ClockWriter) that variable names were used to name the width and
the height of the panels that were displayed. For example, we saw this coding in the
ClockWriter example in Figure 17:
public class ClockWriter extends JPanel
{ public ClockWriter()
{ int width = 200; // the width of the clock
...
clocks_frame.setSize(width, width);
...
}

public void paintComponent(Graphics g)


{ int width = 200; // the width of the clock
...
g.fillRect(0, 0, width, width);
...
}
}
It is unfortunately that we must declare width twice, as two local variables; we can
make do with one if we change width into a field variable and declare it just once,
like this:
4.4. OBJECTS WITH STATE: FIELD VARIABLES 131

public class ClockWriter extends JPanel


{ private int width = 200; // the field remembers the panel’s width

public ClockWriter()
{ ...
clocks_frame.setSize(width, width);
...
}

public void paintComponent(Graphics g)


{ ...
g.fillRect(0, 0, 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

paintComponent method is started, the variable will be reinitialized at its start-


ing count. If the variable is declared within the constructor method, then the
paintComponent method is not allowed to reference it.
Indeed, we have taken for granted a fundamental principle of variables: a variable
declared within a method can be used only by the statements within that method. Such
variables are called local variables, because they are “local” to the method in which
they are declared. Clearly, a local variable cannot be shared by multiple methods in
an object.
For the example application, we desire a variable that is initialized by the construc-
tor method and is read and updated by the paintComponent method. The solution is
to declare the variable outside of both the constructor and paintComponent methods
as a field variable. Figure 18 shows how this is done with a field named count.
Field count’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).” Because
count is declared independently from the methods in the class, it is shared by all the
methods, meaning that the methods can fetch and change its value.
A field should always appear with an internal comment that explains the field’s
purpose and restrictions on its range of values and use, if any. (Such restrictions are
sometimes called the field’s representation invariant.) In the Figure, the comment
reminds us that count’s value should always be nonnegative. This information must
be remembered when we write the methods that reference and assign to the field.
The constructor method of Figure 18 sets count to zero, to denote that the window
has not yet been painted; it does not redeclare the variable—it merely assigns to it.
Each time a message is sent to paint, the paint method increments the value in count
(it does not redeclare the variable—it merely reads and assigns to it) and displays the
new value.
Here is a picture of how a FieldExample object looks in primary storage after its
constructor method has finished:

FieldExample a2 : Graphics a3 : JFrame


main(...) setColor(...) { ... } setSize(...) { ... }
{ ... > } drawString(...) { ... } setVisible(...) { ... }
...
...

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

Figure 4.18: a field variable


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; for this
// reason, its value must always be nonnegative.

/** FieldExample constructs the window. */


public FieldExample()
{ count = 0; // the window has never been painted
// construct the panel’s frame and display it:
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
int height = 200;
my frame.setSize((3 * height)/2, height);
my frame.setVisible(true);
}

/** paintComponent paints the the count of paintings


* @param g - the graphics pen */
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);
}

/** 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

/** Constructor ClockWriter2 constructs the properly sized frame */


public ClockWriter2()
{ ... // the constructor method remains the same
}

/** paintComponent draws the clock with the correct time.


* @param g - the graphics pen that does the drawing */
public void paintComponent(Graphics g)
{ ...
int minutes_angle = 90 - (time.get(Calendar.MINUTE) * 6);
int hours_angle = 90 - (time.get(Calendar.HOUR) * 30);
... // the remainder of the method stays the same as in Figure 17
}

...
}

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

by an arrow whose head is a diamond:

ClockWriter2 JPanel Graphics


paint setColor
drawString

GregorianCalendar
get

Remember these crucial facts about the semantics of fields:


• The field’s cell is created when the object itself is constructed. If there is an
initial value for the field, the value is computed and assigned when the cell is
created. The cell lives inside the object, and the field is ready for use when the
constructor method starts execution.
• A field can be initialized when it is declared, e.g.,

private int width = 200;

If a field is not initialized, like count in Figure 18, the Java interpreter will
insert an initial value:

– Fields that hold numeric values are set to 0


– Fields that hold boolean values are set to false
– Fields that hold (addresses of) objects are set to null (“no value”)

If a field is not initialized when it is declared, it is best to initialize it within


the constructor method.

• 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.)

4.4.1 Using Fields to Remember Inputs and Answers


We finish the chapter by combining the techniques we have learned into an application
that reads interactive input, computes an answer, and paints the answer into a graph-
ics window. Although the resulting application has a less-than-ideal architecture, we
pursue it anyway, because it motivates the materials in the next chapter.
Returning to the Celsius-to-Fahrenheit application in Figure 2, we rewrite it to
display its answers in a graphics window. That is, the application generates the dialog
seen earlier in the chapter:

and in response to the user’s input, a graphics window is constructed that displays
138

the results:

The application’s algorithm is simple:

1. Generate a dialog the reads the input Celsius temperature;

2. Compute the corresponding Fahrenheit temperature;

3. Show a graphics window with the two temperatures.

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

Figure 4.19: a panel for calculating and displaying temperatures


import java.awt.*;
import javax.swing.*;
import java.text.*;
/** CelsiusToFahrenheitWriter creates a panel that displays a
* Celsius temperature and its Fahrenheit equivalent. */
public class CelsiusToFahrenheitWriter extends JPanel
{ int frame height = 200;
{ private int celsius; // the input Celsius temperature
private double fahrenheit; // a Fahrenheit temperature: its value must
// be the translation of celsius into Fahrenheit
private int LEFT MARGIN = 20; // left margin for printing text
private int LINE HEIGHT = 20; // the height of one line of text

/** Constructor CelsiusToFahrenheitWriter receives the user’s input


* Celsius temperature, translates it, and displays the output. */
public CelsiusToFahrenheitWriter()
{ // read the input temperature and compute its translation:
String input =
JOptionPane.showInputDialog("Type an integer Celsius temperature:");
celsius = new Integer(input).intValue();
fahrenheit = ((9.0 / 5.0) * celsius) + 32;
// construct the frame and show the answer:
JFrame f = new JFrame();
f.getContentPane().add(this);
f.setTitle("Celsius to Fahrenheit");
f.setSize((3 * frame height) / 2, frame height);
f.setVisible(true);
}

/** paintComponent paints the panel with the two temperatures


* @param g - the panel’s graphics pen */
public void paintComponent(Graphics g)
{ g.setColor(Color.white); // paint the background:
g.fillRect(0, 0, (3 * frame height) / 2, frame height);
g.setColor(Color.red);
int first line = LINE HEIGHT * 4; // where to print the first line
g.drawString("For Celsius degrees " + celsius + ",", LEFT MARGIN,
first line);
DecimalFormat formatter = new DecimalFormat("0.0");
g.drawString("Degrees Fahrenheit = " + formatter.format(fahrenheit),
LEFT MARGIN, first line + LINE HEIGHT);
}

public static void main(String[] args)


{ new CelsiusToFahrenheitWriter(); }
}
140

in celsius and fahrenheit, respectively. Only then does the constructor method do
its usual business of constructing the graphics window.

4.4.2 Scope of Variables and Fields


There are two forms of variables in Java programs: local variables and field variables.
Local variables are declared and used only inside the body of a method; field variables
are declared independently of a class’s methods and are used by all the methods. The
“area of usage” of a variable is called its scope.
Here is the technical definition of “scope” for local variable declarations in Java:

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);
}

The scope of margin’s declaration are these statements:


int line_height = 20;
int first_line = 40;
int baseline = first_line + (line_height * count);
g.drawString("Painted " + count + " times", margin, baseline);
}

Thus, margin cannot be referenced or changed in the constructor method in Figure


18 nor in any of the statements in paintComponent that precede the declaration.
The scope definition for local variables appears to have a problem—consider this
example:
public static void main(String[] args) // This method is erroneous!
{ int n = 2;
System.out.println(n);
double n = 4.1; // the scope of double n overlaps that of int n !
System.out.println(n); // which n is printed?!
}
4.4. OBJECTS WITH STATE: FIELD VARIABLES 141

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
}

There is also a precise definition of scope for a field variable:

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);
}

private String s = "X" + d;

public void paintComponent(Graphics g)


{ System.out.println(d + " " + s); }
}

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.

4.5 Testing a Program that Uses Input


A programmer quickly learns that merely because a program passes the scrutiny of
the Java compiler, it is not the case that the program will always behave as intended.
For example, perhaps we were editing the temperature-conversion program in Fig-
ure 19 and mistakenly typed fahrenheit = ((9.0 * 5.0) * celsius) + 32 when we
really meant fahrenheit = ((9.0 / 5.0) * celsius) + 32. The resulting program
will compile and produce wrong answers for its inputs. Therefore, a programmer has
the obligation of testing a program for possible errors.
Program testing proceeds much like a tool company tests its new tools: A tool
is tried on a variety of its intended applications to see if it performs satisfactorily or
if it breaks. Of course, a tool cannot be tested forever, but if a tool works properly
in testing for some extended period, then the tool company can confidently issue
a guarantee with the tools it sells. Such a guarantee typically lists the tolerances
and range of activities for which the tool will properly behave and warns the tool’s
user that performance cannot be guaranteed outside the recommended tolerances and
activities.
To test a computer program, one supplies various forms of input data and studies
the resulting outputs. The person testing the program might go about this in two
ways:

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.

In practice, a combination of glass-box and black-box testing is used.


The primary purpose of testing is to identify errors in a program and repair them,
but testing also identifies the limits under which a program might be expected to
behave properly. For example, a change-making program must behave correctly for
positive dollars-and-cents inputs, but if it misbehaves with ridiculous inputs, e.g. a
negative dollars value, it is not a disaster—the program can be given to its users with
the caveat that correct behavior is guaranteed for only precisely defined, reasonable
forms of input data.
Unfortunately, even systematic testing may miss a problematic combination of
input data, so testing a program never brings complete confidence in the program’s
performance. Like the guarantee accompanying the newly purchased tool, the guar-
antee with a newly purchased program is inevitably limited.
The previous sentence should make you uneasy—perhaps there is a way of validat-
ing a program so that it is certain to work correctly for all possible inputs? Indeed,
one can employ techniques from mathematical logic to validate a program in this way,
but the effort is nontrivial. Nonetheless, some aspects of logical validation of code
will be used in subsequent chapters to aid the program development process.

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 class TestPanel extends JPanel


{ ... }

• constructor method (from Figure 17):

/** ClockWriter draws a clock in a panel. */


public class ClockWriter extends JPanel
{ public ClockWriter()
{ ... }

...
}

• object self-reference (from Figure 17):

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

• output view: the component(s) of an application that display output 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).

• constructor method: a method within a class that is executed when an object is


constructed from the class. The constructor method’s name must be the same
as the name of the class in which it appears.

• inheritance: a technique for connecting two classes together so that one is a


“customization” or extension of another. If class C2 inherits/extends class C1,
then any object constructed from class C2 will have all the structure defined
within class C1 as well as the structure within class C2.

• subclass and superclass: if class C2 extends C1, then C2 is a subclass of C1 and


C1 is a superclass of C2.

• this: a special Java value that denotes “this object.”

• painting (a window): drawing text, colors, and shapes on a graphics window;


done with the paintComponent method.

• graphics pen: the object used by a window’s paint method for painting

• frame: Java’s name for an application’s graphics window.

• pixel: one of the “dots” on the display screen; used as a measurement unit for
distances within a window.

• field variable: a variable declared in a class independently of the class’s methods.


A field can be referenced and assigned to by all methods in the class.

• is-a relationship: if class C2 extends class C1, we say that C2 is a C1.

• uses-a relationship: if class C2 sends messages to objects constructed from class


C1, we say that C2 uses a C1.

• has-a relationship: if class C2 possesses a field whose value is a C1-object that


it constructed, we say that C2 has a C1.
146

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.

• An application can display texts, colors, and shapes in a graphics window.


Graphics can be drawn on a panel, constructed from class JPanel. A panel
must be inserted into a frame, created from class JFrame, to be displayed.
We use inheritance to customize JPanel so that it displays graphics. The usual
customizations are

– 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.

New Classes for Later Use

• JOptionPane: Found in package javax.swing. Helps construct dialogs with its


methods,

– showInputDialog(PROMPT): displays the string, PROMPT, and returns the


string typed by the user
– showMessageDialog(null, MESSAGE): displays the string, MESSAGE, to the
user

• class JPanel: Found in package javax.swing. Constructs an empty compo-


nent, but is simply extended by inheritance to display graphics. Table 21.

• class JFrame: Found in package javax.swing. Constructs empty, zero-sized


graphics windows, but it can be sent messages to set its size and display itself.
Some of its methods are listed in Table 21.

• Graphics: A class found in package java.awt. The graphics pen used in a


window’s paint method is in fact an object constructed from class Graphics.
Some of the class’s methods are listed in Table 16.
4.7. PROGRAMMING PROJECTS 147

4.7 Programming Projects


1. For each of the programming projects you completed from the previous chapter,
Chapter 3, revise that project to use interactive input. For example, perhaps
you built the application that computes the distance an automobile travels
based on its initial velocity, Vi , its acceleration, a, and the time, t: distance =
Vi t+(1/2)a(t2 ) Convert that application into an interactive one: When it starts,
the application greets its user as follows:

Please type an initial velocity, in miles per hour (a double):

When the user enters a number, the application replies,

Next, please type the automobile’s acceleration (a double):

and

Finally, please type the elapsed time in hours (a double):

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

The guess was true---the answer is 143.

3. In the java.lang package there is a method named Math.random: using Math.random()


produces a (pseudo-)random double between 0.0 and 1.0. Use this method to
modify the multiplication-drill program in the previous exercise so that the
program randomly picks the two integers that the child must multiply. For
example, the program says:

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 price of the house


• the down payment on the loan for the house
• The number of years for repaying the loan.
• the monthly utility expenses

In response, the application prints

• 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

• monthly utility costs


• weekly costs for entertainment, including lunches, coffees, etc.

The program produces as its output these numbers:

• the total monthly expenses


• the difference—positive or negative—between the money budgeted for ex-
penses and the actual expenses

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

(a) 4 concentric circles, drawn in the center of the window


(b) a transparent, 3-dimensional cube
(c) three eggs, stacked on top of one another
(d) a “snowman,” that is, three white spheres crowned by a black top hat
(e) a 4 by 4 red-and-black chessboard
(f) a “bull’s eye,” that is, 5 alternating red and yellow concentric circles, on a
black background
(g) the playing card symbols, spade, heart, diamond, and club (Hint: Build a
heart from a filled arc topped by two filled ovals; use similar tricks for the
other symbols.)
(h) a Star of David, drawn in gray on a white background
(i) a silly face in various colors
(j) a pink house with a red door resting on green grass with a blue sky (Hint:
for the house’s roof, use a partially filled oval.)
(k) your initials in blue block letters on a yellow background
7. As noted in Chapter 2, this complex Java statement,

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:

(The time displayed is 11:10.)


9. To draw a line of length L, at an angle of D degrees, starting at position x,y, we
must calculate the end point of the line, x’,y’:
x’,y’
/
/ (angle D)
/
x,y . . . . . .

Here are the equations to do this:


x’ = x + L*(cosine( (D*PI)/180 ) )
y’ = y - L*(sine( (D*PI)/180 ) )

(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

Figure 4.20: methods for JFrame


class JFrame
Constructor
JFrame() constructs an empty frame of size 0-by-0 pixels
Method
getGraphics() Returns as its answer the (address of) this frame’s
graphics pen
paint(Graphics g) paints on the frame with the graphics pen, g
repaint() grabs the graphics pen and sends it in a message to
paint.
setLocation(int x, int sets the frame’s location on the display so that its upper
y) left corner appears at location x, y
setSize(int width, int sets the frame’s size to width by height, in pixels
height)
setTitle(String title) sets the frame’s title to title
setVisible(boolean yes) If yes has value true, the frame appears on the display;
if yes has value false, the frame disappears

4.8 Beyond the Basics


4.8.1 Partial API for JFrame

4.8.2 Methods for GregorianCalendar

4.8.3 Colors for Graphics

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.

4.8.1 Partial API for JFrame

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:

JFrame f = new JFrame();


152

Figure 4.21: class GregorianCalendar


class GregorianCalendar

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.

Graphics pen = f.getGraphics();


pen.drawString("Hello from main!", 100, 100);

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.

4.8.2 Methods for GregorianCalendar

For several chapters we have made good use of class GregorianCalendar. A summary
of its most useful methods appears in Table 21.

4.8.3 Colors for Graphics


Here are the basic colors you can use to color the background of a window and fill
a graphics pen: black, blue, cyan, darkGray, gray, green, lightGray, magenta,
orange, pink, red, yellow, white. All must be prefixed by Color., e.g., Color.orange.
4.8. BEYOND THE BASICS 153

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.

You can declare variables to hold (addresses of) colors:

private Color BACKGROUND_COLOR = Color.yellow;

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.

Thanks to the World-Wide-Web protocol, a web browser can locate HTML-


formatted files on other computers in foreign locations. If an HTML-file uses an
applet, the file along with the applet are copied into the web browser for display. In
this regard, an applet is a “mobile program,” moving from browser to browser.
154

For practice, let’s make class MyPanel into an applet:

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

Figure 4.22: The HTML-file ¡tt¿DisplayApplet.html¡/tt¿


<head>
<title>My Graphics Window </title>
</head>
<body>
Here is my graphics window as an applet:
<p>
<applet code = "MyApplet.class" width=300 height=200>
Comments about the applet go here; these are shown on the web
page if the applet cannot be displayed. </applet>
</body>

Figure 23 shows the applet; compare it to Figure 12.


Remember to compile MyApplet.java so that MyApplet.class is found in the same
folder (directory) as DisplayApplet.html When a web browser reads DisplayApplet.html,
the browser will execute MyApplet.class, displaying the applet.
Here is a comment about testing applets: A web browser copies an applet only
once, so updating an applet and refreshing the HTML-file that uses it will not show
the updates. It is better to test and revise an HTML page and its applet with a
testing program called an appletviewer. (If you use an IDE, set it to test applets; if
you use the JDK, use the command appletviewer DisplayApplet.html.) Once the
applet works to your satisfaction, only then use the web browser to display it.
Finally, you are warned that there are many different versions of web browsers,
and not all web browsers understand all the packages of the Java language. For
this reason, applets that tested correctly with an appletviewer may misbehave when
started from a web browser, and there is little one can do about it.
156

Figure 4.23: a simple applet


import java.awt.*;
import javax.swing.*;
/** MyApplet displays a window with geometric figures */
public class MyApplet extends JApplet
{
// An applet has no constructor method---instead, it has an init method:
/** init constructs the applet for display */
public void init()
{ }

// paintComponent is renamed paint:


/** paint fills the window with the items that must be displayed
* @param g - the ‘‘graphics pen’’ that draws the items onto the window */
public void paint(Graphics g)
{ int frame width = 300;
int frame height = 200;
g.setColor(Color.white); // paint the white background:
g.fillRect(0, 0, frame width, frame height);
g.setColor(Color.red);
int left edge = 105; // the left border where the shapes appear
int top = 70; // the top where the shapes appear
// draw a rectangle:
int width = 90;
int height = 60;
g.drawRect(left edge, top, width, height);
// draw a filled circle:
int diameter = 40;
g.fillOval(left edge + width - diameter, top, diameter, diameter);
}
}
Chapter 5

Component Structure: Method and


Class Building
5.1 Methods

5.2 Public Methods

5.2.1 Basic Public Methods

5.2.2 Constructor Methods

5.3 Parameters to Methods

5.3.1 Forms of Parameters

5.4 Case Study: General-Purpose Output Frame

5.5 Results from Methods: Functions

5.6 Private Methods

5.7 Summary

5.8 Programming Projects

5.9 Beyond the Basics

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 write classes containing methods that we use over and over;

• 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
}

public void paintComponent(Graphics g)


{ ... // instructions for painting on the panel
}
}

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.

5.2 Public Methods


Consider this well-used statement:
System.out.println("TEXT");
The statement sends a println("TEXT") message to the object System.out, asking
System.out to execute its method, println.
The object that sends the message is called the client, and the object that receives
the message is the receiver (here, System.out). The receiver locates its method whose
name matches the one in the message and executes that method’s statements—we
say that the method is invoked. (Here, System.out locates its println method and
executes it, causing TEXT to appear in the command window; we say that println is
invoked.)
An application can send println messages to System.out because println is a
public method, that is, it is available to the “general public.”
We have used many such public methods and have written a few ourselves, most
notably, main and paintComponent. Consider again the format of the latter, which we
use to paint graphics on a panel:
public void paintComponent(Graphics g) // this is the header line
{ STATEMENTS // this is the body
}
The syntax is worth reviewing: The method’s header line contains the keyword,
public, which indicates the general public may send paintComponent messages to
the graphics window that possesses this method. (In this case, the “general public”
includes the computer’s operating system, which sends a paintComponent message
each time the graphics window must be repainted on the display.) The keyword,
void, is explained later in this chapter, as is the formal parameter, Graphics g. The
statements within the brackets constitute the method’s body, and these statements
are executed when the method is invoked.
A public method must be inserted within a class. As we saw in Chapter 4, a class
may hold multiple methods, and a class may hold fields as well as methods. When
an object is constructed from the class, the object gets the fields and methods for its
own. Then, other objects can send messages to the object to execute the methods.
160

We begin with some basic examples of public methods.

5.2.1 Basic Public Methods


A public method that we write should be capable of one basic “skill” or “behavior”
that clients might like to use. Here is an example that illustrates this principle. (To
to help us focus on crucial concepts, we dispense with graphics for the moment and
work with basic techniques from Chapter 2.)
Pretend that you collect “Ascii art” insects, such as bees, butterflies, and ladybugs
(“ladybirds”):
,-.
\_/
>{|||}-
/ \
‘-^ hjw

_ " _
(_\|/_)
(/|\) 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();
}

We always begin a method with a comment—the method’s specification—that doc-


uments the method’s purpose or behavior. The method’s header line comes next; it
states that the method is public and gives the method’s name, which we invent. For
5.2. PUBLIC METHODS 161

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:

AsciiArtWriter writer = new AsciiArtWriter();

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:

public class DrawArt2


{ public static void main(String[] args)
{ AsciiArtWriter w = new AsciiArtWriter();
w.printButterfly();
162

Figure 5.1: class that draws Ascii art


/** AsciiArtWriter contains methods for drawing Ascii art */
public class AsciiArtWriter
{ /** Constructor AsciiArtWriter does an ‘‘initialization.’’ */
public AsciiArtWriter()
{ System.out.println(); }

/** printBee prints a bee */


public void printBee()
{ System.out.println(" ,-.");
System.out.println(" \\ /"); // the \ must be written as \\
System.out.println(">{|||}-");
System.out.println(" / \\");
System.out.println(" ‘-^ hjw");
System.out.println();
}

/** printButterfly prints a butterfly */


public void printButterfly()
{ System.out.println(" \""); // the " must be written as \"
System.out.println(" ( \\|/ )");
System.out.println(" (/|\\) ejm97");
System.out.println();
}

/** printLadybug prints a ladybug */


public void printLadybug()
{ System.out.println(" ‘m\’"); // the ’ must be written as \’
System.out.println(" (|) sahr");
System.out.println();
}
}
5.2. PUBLIC METHODS 163

Figure 5.2: application that prints Ascii art


/** DrawArt prints some Ascii art and a message */
public class DrawArt
{ public static void main(String[] args)
{ AsciiArtWriter writer = new AsciiArtWriter();
writer.printBee();
System.out.println("This is a test.");
writer.printButterfly();
writer.printBee();
}
}

w.printButterfly();
}
}

What is printed on the display?


2. Explain what appears in the command window when this application is exe-
cuted. How many AsciiArtWriter objects are constructed?

public class TestArt


{ public static void main(String[] args)
{ AsciiArtWriter writer = new AsciiArtWriter();
writer.printBee();
new AsciiArtWriter().printButterfly();
writer.printLadyBug();
}
}

3. Here is a class with a public method:

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!");
}

public class HelperClass


{ ...
/** readNameAndDisplayItsLength reads one name and displays the
* name with its length
* Hint: for a string, x, x.length() returns x’s length */
...
}

5.2.2 Constructor Methods


A constructor method is a special case of a public method. When we construct an
object from a class, e.g.,
AsciiArtWriter writer = new AsciiArtWriter();

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.

/** FieldExample constructs the window. */


public FieldExample()
{ count = 0; // the window has never been painted
JFrame my_frame = new JFrame();
my_frame.getContentPane().add(this);
int height = 200;
my_frame.setSize((3 * height)/2, height);
my_frame.setVisible(true);
}
... // See Figure 18, Chapter 4, for the rest of the class
}

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()
{ }

When a new AsciiArtWriter() object is constructed, the do-nothing constructor


method is invoked and does nothing. The Java language allows you to write a class
that has no constructor method at all, but this style of programming will not be
followed in this text.
166

Execution Trace of Method Invocation


To reinforce our intuitions regarding method invocation, we now study the steps the
Java interpreter takes when it executes the application in Figure 2.
When the application is started, a DrawArt object appears in primary storage, and
its main method is started:
DrawArt
main
{ 1 > AsciiArtWriter writer = new 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.");
...
}

In this way, the messages to writer are executed one by one.

Exercises
1. Here is a class with a constructor and public method:

public class PrintClass


{ public PrintClass()
{ System.out.println("A"); }

public void printB()


{ System.out.println("B"); }
}

What is printed by this application?

public class TestPrintClass


{ public static void main(String[] args)
{ PrintClass p = new PrintClass();
p.printB();
p.printB();
new PrintClass().printB();
PrintClass q = p;
}
}
168

2. Here is a class with a constructor and public method:

public class Counter


{ private int count;

public Counter()
{ count = 0; }

public void count()


{ count = count + 1;
System.out.println(count);
}
}

What is printed by this application?

public class TestCounter


{ public static void main(String[] args)
{ Counter a = new Counter();
a.count();
a.count();
Counter b = new Counter();
b.count();
a.count();
Counter c = a;
c.count();
a.count();
}
}

5.3 Parameters to Methods


When an object is sent a message, the message often contains additional information
in parentheses, called arguments. We have used arguments in messages many times;
the standard example is System.out.println("TEXT"), which sends the argument,
"TEXT", in its message to the System.out object.
The technical name for an argument is actual parameter, or parameter, for short.
When an object receives a message that includes actual parameters, the object gives
the parameters to the method named in the message, and the method binds the
parameters to local variables. We can see this in the following example.
Say that we wish to attach names to the bees that we draw with the AsciiArtWriter.
For example, this bee is named “Lucy”:
5.3. PARAMETERS TO METHODS 169

Figure 5.3: method with a parameter


/** printBeeWithName prints a bee with a name attached to its stinger.
* @param name - the name attached to the stinger */
public void printBeeWithName(String name)
{ System.out.println(" ,-.");
System.out.println(" \\ /");
System.out.println(">{|||}-" + name + "-");
System.out.println(" / \\");
System.out.println(" ‘-^ hjw");
System.out.println();
}

,-.
\_/
>{|||}-Lucy-
/ \
‘-^ hjw

To do this, we modify method printBee from Figure 1 so that it attaches a name


to its bee by means of a parameter. The method’s header line changes so that it
contains information about the parameter within its brackets:
public void printBeeWithName(String name)

The information in the parentheses, String name, is actually a declaration of a local


variable, name. The declaration is called a formal parameter—it binds to (is assigned
the value of) the incoming argument (actual parameter).
To understand this, say that we add the modified method in the Figure to class
AsciiArtWriter, and we invoke it as follows:

AsciiArtWriter writer = new AsciiArtWriter();


writer.printBeeWithName("Lucy");

The invocation, writer.printBeeWithName("Lucy"), gives the actual parameter, "Lucy",


to the method, printBeeWithName, which generates this initialization inside of a new
local variable inside the method’s body:
String name = "Lucy";

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");

prints a bee similar to Lucy’s bee but with a different name.


We can write a method like printBeeWithName in advance of its invocations, be-
cause the method need not know exactly the name it attaches to the bee it draws,
but it assumes that the formal parameter—a variable—will contain the value when
the time comes to draw the bee.
As part of our documentation policy, the comments preceding the method include
a short description of each parameter and its purpose. For reasons explained at the
end of the chapter, we begin each parameter’s description with the text, * @param.
When printBeeWithName is invoked, it must be invoked with exactly one actual pa-
rameter, which must compute to a string. This was the case with writer.printBeeWithName("Lucy"),
but we might also state writer.printBeeWithName("Fred" + "Mertz"), whose argu-
ment is an expression that computes to a string, or we might use variables in the
argument:

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:

{ String name = "Ricky Ricardo";


System.out.println(" ,-.");
System.out.println(" \\_/");
System.out.println(">{|||}-" + name + "-");
System.out.println(" / \\");
System.out.println(" ‘-^ hjw");
}

This example emphasizes that

Parameter binding works like initialization of a local variable—the actual parameter


initializes the variable (formal parameter), and the local variable is used only
within the body of the method where it is declared.

If we would attempt the invocation, writer.printBeeWithName(3), the Java com-


piler would announce that a data-type error exists because the actual parameter has
data type int, whereas the formal parameter has data type String. If we truly wanted
to attach 3 as the name of the bee, then we must convert the integer into a string, say
by, writer.printBeeWithName(3 + ""), which exploits the behavior of the + operator.
5.3. PARAMETERS TO METHODS 171

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 ...
... }
...
}

The initialization executes as usual,


DrawArt a1 : AsciiArtWriter
main ...
{ AsciiArtWriter writer == a1 printBeeWithName
String s == "Ricardo" { String name = "Ricky Ricardo";
1> AWAIT COMPLETION OF METHOD 2> System.out.println(...);
... ...
} }
...

and at this point, printBee’s body behaves like any other method.
172

Exercises
1. What will this application print?

public class PrintClass


{ public PrintClass() { }

public void printName(String name)


{ String s = " ";
System.out.println(name + s + name);
}
}

public class TestPrintClass


{ public static void main(String[] args)
{ String s = "Fred";
new PrintClass().printName(s + s);
}
}

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

/** Constructor NameClass initializes a NameClass object


* @param n - the name that is to be remembered */
public NameClass(String n)
{ ... }

/** printName prints the name remembered by this object */


public void printName()
{ ... }

/** displayLength prints the integer length of the name remembered.


* (Hint: use the length method from Table 5, Chapter 3.) */
public void displayLength()
{ ... }
}

public class TestNameClass


5.3. PARAMETERS TO METHODS 173

{ public static void main(String[] args)


{ NameClass my_name = new NameClass("Fred Mertz");
System.out.print("my name is ");
my_name.printName();
System.out.print("my name has this many characters in it: ");
my_name.displayLength();
}
}

5.3.1 Forms of Parameters


What can be a parameter? The answer is important:

Any value that can be assigned to a variable can be an actual parameter.

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:

import java.text.*; // Note: This statement is inserted at the beginning


// of the class in which the following method appears.

/** printInverse prints the inverse, 1.0/i, of an integer, i,


* formatted to three decimal places.
* @param i - the integer that is inverted */
public void printInverse(int i)
{ DecimalFormat formatter = new DecimalFormat("0.000"); // see Chapter 4
double d = 1.0 / i;
String s = formatter.format(d); // formats d with three decimal places
System.out.println(s);
}

Parameter i is declared as an integer, so any invocation of inverseOf must use an


actual parameter that computes to an integer. (The Java compiler verifies this when
it checks the data type of the actual parameter in the invocation.) The method
computes the fractional inverse of i and formats it into three decimal places, using a
helper object constructed from class DecimalFormat, found in the package java.text
and introduced in Chapter 4. The formatted result is printed.
Say that we insert inverseOf into some new class, say, class MathOperations.
This lets us do the following:

MathOperations calculator = new MathOperations();


calculator.printInverse(3);
174

Figure 5.4: method with multiple parameters


import java.text.*; // Note: This statement is inserted at the beginning
// of the class in which the following method appears.

/** printInverse prints the inverse, 1.0/i, of an integer, i.


* @param i - the integer that is inverted
* @param pattern - the pattern for formatting the fractional result */
public void printInverse(int i, String pattern)
{ DecimalFormat formatter = new DecimalFormat(pattern); // see Chapter 4
double d = 1.0 / i;
String s = formatter.format(d);
System.out.println(s);
}

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)

Then the invocation,


calculator.printInverse(0.5);

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;

which is acceptable in Java, because the 3 is converted into 3.0.


Methods may receive multiple parameters, and we can use this extension to good
effect in the above example. Say that we alter printInverse so that its client can
specify the pattern of precision used to print the inverse. The method now receives
two parameters; see Figure 4.
The parameters are stated in the header line, between the brackets, separated by
commas. When the method is invoked, there must be exactly two actual parameters,
and the first must be an integer and the second must be a string:
5.3. PARAMETERS TO METHODS 175

MathOperations calculator = new MathOperations();


calculator.printInverse(3, "0.00000"); // print with five decimal places
calculator.printInverse(5, "0.0"); // print with one decimal place

The important point to remember is

Actual parameters bind to formal parameters in order—the first actual parameter


binds to the first formal parameter, the second actual parameter binds to the
second formal parameter, and so on.

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

import java.text.*; // Note: This statement is inserted at the beginning


// of the class in which the following method appears.

/** printInverse prints the inverse, 1.0/i, of an integer, i.


* @param i - the integer that is inverted
* @param f - the object that formats the fractional result */
public void printInverse(int i, DecimalFormat f)
{ double d = 1.0 / i;
String s = f.format(d);
System.out.println(s);
}
176

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:

MathOperations calculator = new MathOperations();


DecimalFormat five_places = new DecimalFormat("0.00000");
calculator.printInverse(3, five_places);

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

public void paintComponent(Graphics g)

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:

public static void main(String[] args)

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.

• A method whose header line has the form,

public void METHODNAME()

is a method with zero formal parameters and must be invoked by a statement


that uses zero actual parameters within the parentheses—RECEIVER OBJECT.METHODNAME()—
this is why an empty bracket set is required with some invocations.
5.3. PARAMETERS TO METHODS 177

Exercises
1. Here is a helper class:

public class ArithmeticClass


{ private int base;

public ArithmeticClass(int b)
{ base = b; }

public void printMultiplication(String label, double d)


{ System.out.println(label + (base * d)); }
}

(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);
}
}

(b) Explain the errors in this application:


public class Errors
{ public static void main(String[] args)
{ ArithmeticClass c = new ArithmeticClass();
printMultiplication("A", 5);
int s = 0;
c.printMultiplication(s, 2 + s);
c.printMultiplication(1, "A");
c.printMultiplication("A", 9, 10);
c.printSomething();
}
}

2. Write the missing methods for this class:

/** RunningTotal helps a child total a sequence of numbers */


public class RunningTotal
{ private int total; // the total of the numbers added so far
178

/** Constructor RunningTotal initializes the object */


public RunningTotal()
{ total = 0; }

/** addToTotal adds one more number to the running total


* @param num - the integer to be added to the total */
...

/** printTotal prints the current total of all numbers added so far */
...
}

public class AddSomeNumbers


{ public static void main(String[] args)
{ RunningTotal calculator = new RunningTotal();
calculator.addToTotal(16);
calculator.addToTotal(8);
calculator.printTotal();
calculator.addToTotal(4);
calculator.printTotal();
}
}

3. The paintComponent method of the clock-writing class in Figure 17, Chapter 4,


can be rewritten so it uses this method:

/** paintClock paints a clock with the time


* @param hours - the current hours, an integer between 1 and 12
* @param minutes - the current minutes, an integer between 0 and 59
* @param x - the upper left corner where the clock should appear
* @param y - the upper right corner where the clock should appear
* @param diameter - the clock’s diameter
* @param g - the graphics pen used to paint the clock */
public void paintClock(int hours, int minutes, int x, int y,
int diameter, Graphics g)

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

5.4 Case Study: General-Purpose Output Frame


In practice, we design public methods when we are designing a class: We consider
what the responsibilities (behaviors) of the class might be, and we design public
methods for each of the expected behaviors. We write the class with the necessary
private fields and constructor methods so that the public methods operate properly.
A major component of our applications has been the “output view”—the part
that displays the computation results. We can reduce our dependency on System.out
as our output-view object if we design our own graphics window to display textual
output. Perhaps we write an application that asks its user to type input text:

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

Figure 5.5: specification of an output frame

MyWriter creates a graphics win-


dow that displays a sentence.
Constructor:
MyWriter(int w, int h) construct the window with the width of w pixels and
the height of h pixels and display it.
Methods:
writeSentence(String s) display sentence s in the graphics window
repositionSentence(int redisplay the existing sentence at the new position,
new x, int new y) new x, new y
Private attributes:
sentence the sentence that is displayed
x position the x-coordinate of where the sentence will appear
y position the y-coordinate of where the sentence will appear

private String sentence; // holds the sentence to be displayed

and have paintComponent display the field’s contents:

public void paintComponent(Graphics g)


{ ...
g.drawString(sentence, ... );
}

Now writeSentence(String s) has this algorithm:

1. assign sentence = s;

2. make paintComponent execute.


We do Step 2 by invoking a prewritten method already contained within class
JPanel: It is called repaint, and it invokes paintComponent with the panel’s the
graphics pen object as the actual parameter, just like the computer’s operating sys-
tem does whenever a panel must be repainted. The invocation of repaint forces the
panel to repaint, even if it is not iconified or moved or covered. The coding reads:

public void writeSentence(String s)


{ sentence = s;
this.repaint(); // indirectly forces paintComponent to execute
}

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

method. To invoke the method within MyWriter, we write this.repaint(), because


the receiver of the invocation is this object—the client object that sends the message
is also the receiver!
The completed class MyWriter appears in Figure 6. The class uses a number of
private fields, which remember the window’s size, the position for displaying the sen-
tence, and the sentence itself (which is initialized to an empty string). The constructor
method uses two parameters to size the window.
The paintComponent method operates as expected, and writeSentence deposits
the string to be displayed in field sentence and forces this object to repaint. Finally,
positionSentence resets the fields that position the sentence and forces the sentence
to be redisplayed. We use the invocation,
this.writeSentence(sentence);
to reinforce that an object can send messages to its own public methods.
Begin Footnote: The Java language allows the this pronoun to be omitted from
invocations of an object’s own methods, e.g., writeSentence(sentence) can be used
in the previous example. End Footnote
Note also that the value of a field can be an argument in an invocation. Finally,
positionSentence could also be written as
public void positionSentence(int new_x, int new_y)
{ x_position = new_x;
y_position = new_y;
this.repaint();
}
which has the same behavior.
Here is a small application that uses class MyWriter to interact with its user:
import javax.swing.*;
/** MyExample2 displays, in a graphics window, a sentence its user types */
public class MyExample2
{ public static void main(String[] args)
{ int width = 300;
int height = 200;
MyWriter writer = new MyWriter(width,height);
writer.positionSentence(50, 80); // set position to my liking
String s = JOptionPane.showInputDialog("Please type some text:");
writer.writeSentence(s); // display s
}
}
When the application starts, the graphics window appears with an empty sentence,
which is immediately repositioned to location, 50, 80. Then the interaction displayed
at the beginning of this section occurs.
Here are the lessons learned from this case study:
5.4. CASE STUDY: GENERAL-PURPOSE OUTPUT FRAME 183

Figure 5.6: graphics window for displaying text


import java.awt.*; import javax.swing.*;
/** MyWriter creates a graphics window that displays a sentence */
public class MyWriter extends JPanel
{ private int width; // the frame’s width
private int height; // the frame’s height
private String sentence = ""; // holds the sentence to be displayed
private int x position; // x-position of sentence
private int y position; // y-position of sentence

/** Constructor MyWriter creates the Panel


* @param w - the window’s width
* @param h - the window’s height */
public MyWriter(int w, int h)
{ width = w;
height = h;
x position = width / 5; // set the sentence’s position
y position = height / 2;
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("MyWriter");
my frame.setSize(width, height);
my frame.setVisible(true);
}

/** paintComponent paints the panel


* @param g - the ‘‘graphics pen’’ that draws the items */
public void paintComponent(Graphics g)
{ g.setColor(Color.red);
g.drawString(sentence, x position, y position);
}

/** writeSentence displays a new string in the window


* @param s - the sentence to be displayed */
public void writeSentence(String s)
{ sentence = s;
this.repaint(); // indirectly forces paintComponent to execute
}

/** positionSentence redisplays the existing sentence in a new position


* @param new x - the new horizontal starting position
* @param new y - the new vertical starting position */
public void positionSentence(int new x, int new y)
{ x position = new x;
y position = new y;
this.writeSentence(sentence); // force a rewrite of the existing sentence
}
}
184

1. We designed and wrote a class that can be used as a component of many appli-
cations.

2. We designed a specification that helped us write the class.

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.

• If part an application must be rewritten or replaced, it is easier to do so if the


part is one separate class.

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

3. Add this new method to class MyWriter:

/** writeSecondSentence displays a sentence, t, underneath the first


* sentence in the window.
* @param t - the second sentence to be displayed */
public void writeSecondSentence(String t)

When you add this method, must you revise writeSentence? repositionSentence?

4. Write a class that satisfies this specification:

TextWriter displays up to three


lines of text in a graphics win-
dow
Constructor:
TextWriter(int w, int h) Constructs the window with width w pixels and
height h pixels and displays a window with three
empty lines of text.
Methods:
print1(String s) Appends s to the end of the Line 1 text and
displays the current values of all three lines of
text.
reset1(String s) Sets Line 1 of the text to s and displays the
current values of all three lines of text.
print2(String s) Appends s to the end of the Line 2 text and
displays the current values of all three lines of
text.
reset2(String s) Sets Line 2 of the text to s and displays the
current values of all three lines of text.
print3(String s) Appends s to the end of the Line 3 text and
displays the current values of all three lines of
text.
reset3(String s) Sets Line 3 of the text to s and displays the
current values of all three lines of text.

Test the class with this application:

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);
}
}

Next, rewrite Figure 2, Chapter 4, to use a TextWriter object to display its


output.

5.5 Results from Methods: Functions


When a client object sends a message, sometimes the client expects an answer in
reply. We saw this in Figure 2, Chapter 4, where the controller asked the input-view
to read an input string:
String input =
JOptionPane.showInputDialog("Type an integer Celsius temperature:");
When JOptionPane’s showInputDialog method executes, it fetches the string the user
types, and the string is the “reply,” which is deposited into the position where the
readLine message appears. Then, the string is assigned to variable input.
Yet another example appears in the same figure,
int c = new Integer(input).intValue();
where the object, new Integer(input), is sent the message, intValue(), and replies
with the integer that is assigned to c.
“Replies” from methods are called results. The idea comes from mathematics,
where one speaks of the result from calculating a formula. Indeed, we can take the
well-used temperature conversion formula, f = (9.0 / 5.0) * c + 32, and encode it
as a method that returns a result; see Figure 7. The method illustrates the technique
for returning a result:
• In the method’s header line, replace the keyword, void, by the data type of the
result the method should return. (Here, it is double.) We now note that all the
previous uses of the term, void, meant “returns no result.”
• At the end of the method’s body, insert a return E statement, where E computes
to the value that will be returned. E can be any expression whose data type
matches the one listed in the header line as the result’s data type.
Indeed, the temperature-conversion method can be written just this tersely:
5.5. RESULTS FROM METHODS: FUNCTIONS 187

Figure 5.7: temperature conversion function


/** celsiusIntoFahrenheit translates degrees Celsius into Fahrenheit
* @param c - the degrees in Celsius
* @return the equivalent degrees in Fahrenheit */
public double celsiusIntoFahrenheit(double c)
{ double f = ((9.0 / 5.0) * c) + 32;
return f;
}

public double celsiusIntoFahrenheit(double c)


{ return ((9.0 / 5.0) * c) + 32; }

• As part of our documentation policy, we include the line,

* @return the equivalent degrees in Fahrenheit

in the comments at the head of the method to describe the result computed.

A method that returns a result is sometimes called a function. A function’s commen-


tary includes a line labelled @return, which explains the nature of the result returned
by the function.
If the celsiusIntoFahrenheit was included in some class, say, class TemperatureConvertor,
then we might use it to convert temperatures as follows:
import javax.swing.*;
/** ConvertATemperature converts one Celsius temperature into Fahrenheit */
public class ConvertATemperature
{ public static void main(String[] args)
{ String input =
JOptionPane.showInputDialog("Type an integer Celsius temperature:");
int c = new Integer(input).intValue(); // convert input into an int

TemperatureConvertor convert = new TemperatureConvertor();


double f = convert.celsiusIntoFahrenheit(c);

MyWriter writer = new MyWriter(300, 200);


writer.writeSentence(c + " Celsius is " + f + " Fahrenheit");
}
}

The two statements in the middle of the main method can be compressed into one, if
desired:
188

double f = new TemperatureConvertor().celsiusIntoFahrenheit(c);

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 penultimate statement invokes the celsiusIntoFahrenheit function, the function


computes and returns a result, but the result is ignored and execution proceeds to
the final statement.
This technique is more commonly used with a constructor method, which is a
“function” that automatically returns the address of the constructed object. For
example, class CelsiusToFahrenheitFrame in Figure 14, Chapter 4, is self-contained,
so its start-up application merely reads:
public class Convert
{ public static void main(String[] args)
{ new CelsiusToFahrenheitFrame(); }
}

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

proceeds to its result:


ConvertATemperature a1 : TemperatureConvertor
main celsiusIntoFahrenheit
{ int c == 18
{ double c == 18.0
...
double f == 68.0
1> TemperatureConvertor convert == a1
2> return f;
double f = ?
}
...
1> f = AWAIT RESULT;
...
}

Notice that the variables, c and f, local to celsiusIntoFahrenheit, are unrelated to


the local variables in main—no problems are introduced, no inadvertent connections
are made, merely because the two program components chose similar names for their
local variables.
Finally, the return f statement computes 68.0 and inserts it into the position
where the invocation appeared. The function resets for later invocations:
ConvertATemperature
main
{ int c == 18
...
1> TemperatureConvertor convert == a1
double f = ?
1> f = 68.0;
...
}

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 class Counter


{ private int count;

public Counter(int i)
{ count = i; }
5.5. RESULTS FROM METHODS: FUNCTIONS 191

public int countA()


{ count = count + 1;
return count;
}

public double countB()


{ return count + 1.5; }

public String countC()


{ return "*" + count; }
}

public class TestCounter


{ public static void main(String[] args)
{ Counter c = new Counter(3);
int x = c.countA();
System.out.println(x + " " + c.countB());
System.out.println(c.countC() + c.countA());
}
}

2. Place the function, celsiusIntoFahrenheit, seen in this section into a class,


class TemperatureConvertor. Add this method to the class:

/** fahrenheitIntoCelsius translates degrees Fahrenheit into Celsius


* @param f - the degrees in Fahrenheit, a double
* @return the equivalent degrees in Celsius, a double */

(Hint: use algebra to compute the conversion formula.) Next, text the class
with this application:

public class ConvertTemps


{ public static void main(String[] args)
{ TemperatureConvertor calculator = new TemperatureConvertor();
int temp = new Integer(args[0]).intValue(); // get command-line input
double ftemp = calculator.celsiusIntoFahrenheit(temp);
System.out.println(temp + "C is " + ftemp + "F");
System.out.println("Verify: " + ftemp + "F is "
+ calculator.fahrenheitIntoCelsius(ftemp) + "C");
double ctemp = calculator.fahrenheitIntoCelsius(temp);
System.out.println(temp + "F is " + ctemp + "C");
System.out.println("Verify: " + ctemp + "C is "
+ calculator.celsiusIntoFahrenheit(ctemp) + "F");
}
}
192

3. Write functions that match each of these specifications:

(a) /** kilometersToMiles converts a kilometers amount into miles


* using the formula: Miles = 0.62137 * Kilometers
* @param k - the kilometers amount
* @return the corresponding miles amount */
public double kilometersToMiles(double k)
Insert the function into an application that reads a kilometers value from
the user and prints the value in miles.
(b) /** compoundTotalOf computes the compounded total
* that result from a starting principal, p, an interest rate, i,
* and a duration of n compounding periods, using this formula:
* total = p((1 + (i/n))<sup>n</sup>)
* @param p - the starting principal, a dollars, cents, amount
* @param i - the interest rate per compounding period,
* a fraction (e.g., 0.01 is 1%)
* @param n - the compounding periods (e.g., if compounding is done
* monthly, then two years is 24 compounding periods)
* @return the total of principal plus compounded interest */
public double compoundTotalOf(double p, double i, int n)
(c) /** isDivisibleByNine checks if its argument is divisible by 9 with no remander.
* @param arg - the argument to be checked
* @return true, if it is divisible by 9; return false, otherwise. */
public boolean isDivisibleByNine(int arg)
(d) /** pay computes the weekly pay of an employee
* @param name - the employee’s name
* @param hours - the hours worked for the week
* @param payrate - the hourly payrate
* @return a string consisting of the name followed by "$" and the pay */
public String pay(String name, int hours, double payrate)

4. Here is an application that would benefit from a function. Rewrite it with one.

/** Areas prints the areas of three circles */


public class Areas
{ public static void main(String[] args)
{ System.out.println("For radius 4, area = " + (Math.PI * 4*4));
System.out.println(Math.PI * 8*8);
System.out.println((Math.PI * 19*19) + " is the area for radius 19");
}
}

(Note: Math.PI is Java’s name for the math constant, Pi.)


5.6. PRIVATE METHODS 193

5.6 Private Methods


This Chapter’s introduction mentioned that an object’s methods can be classified as
“public” and “private”: Public methods can be invoked by the general public, but
private methods can be invoked only within the class where they appear.
Why should we bother with writing private methods? Private methods help im-
pose neat internal structure to a class: specific instruction sequences can be collected
together, appropriately named, and they can be invoked multiple times within the
class. We give two examples.

Naming a Subalgorithm with a Private Method


Say that we decide to improve class MyWriter in Figure 6 so that the graphics window
appears with a blue border and a white center:

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

Figure 5.8: graphics window with private method


import java.awt.*; import javax.swing.*;
/** MyWriter2 creates a graphics window that displays a sentence */
public class MyWriter2 extends JFrame
{ private int width; // the frame’s width
private int height; // the frame’s height
private String sentence = ""; // holds the sentence to be displayed
private int x position = 50; // x-position of sentence
private int y position = 80; // y-position of sentence

/** Constructor MyWriter2 creates the window and makes it visible


* @param w - the window’s width
* @param h - the window’s height */
public MyWriter2(int w, int h)
{ ... see Figure 6 ... }

/** paintComponent paints the panel


* @param g - the ‘‘graphics pen’’ that draws the items */
public void paintComponent(Graphics g)
{ makeBorder(g); // invoke private method to paint the border
g.setColor(Color.red);
g.drawString(sentence, x position, y position);
}

/** makeBorder paints the frame’s border.


* @param pen - the graphics pen used to paint the border */
private void makeBorder(Graphics pen)
{ pen.setColor(Color.blue);
pen.fillRect(0, 0, width, height); // paint entire window blue
int border size = 20;
int center rectangle width = width - (2 * border size);
int center rectangle height = height - (2 * border size);
pen.setColor(Color.white);
pen.fillRect(border size, border size, // paint center rectangle white
center rectangle width, center rectangle height);
}

/** writeSentence displays a new string in the window


* @param s - the sentence to be displayed */
public void writeSentence(String s)
{ ... see Figure 6 ... }

/** repositionSentence redisplays the existing sentence in a new position


* @param new x - the new horizontal starting position
* @param new y - the new vertical starting position */
public void repositionSentence(int new x, int new y)
{ ... see Figure 6 ... }
}
5.6. PRIVATE METHODS 195

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.

Repeating an Activity with a Private Method


Chapter 4 showed us how to draw geometric figures in a graphics window. Say that
our current passion is stacking “eggs” in a window, like this:

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:

/** paintAnEgg paints an egg in 3-by-2 proportion


196

* (that is, the egg’s height is two-thirds of its width)


* @param bottom - the position of the egg’s bottom
* @param width - the egg’s width
* @param pen - the graphics pen that will draw the egg
* @return the y-position of the painted egg’s top edge. */

This method’s algorithm goes as follows:


1. Calculate the egg’s height as 2/3 of its width.

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:

(a) new StackedEggsWriter(30, 0, 500);


(b) new StackedEggsWriter(30, -10, 30);
(c) new StackedEggsWriter(-300, 300, 30);

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

Figure 5.9: repeated invocations of a private method


import java.awt.*;
import javax.swing.*;
/** StackedEggsWriter displays three eggs, stacked */
public class StackedEggsWriter extends JPanel
{ private int frame width;
private int frame height;
// the sizes (widths) of the three eggs stacked:
private int egg1 size;
private int egg2 size;
private int egg3 size;

/** Constructor StackedEggsWriter stacks three 3-by-2 eggs in a window


* @param width - the width of the panel
* @param height - the height of the panel
* @param size1 - the width of the bottom egg
* @param size2 - the width of the middle egg
* @param size3 - the width of the top egg */
public StackedEggsWriter(int width, int height,
int size1, int size2, int size3)
{ frame width = width;
frame height = height;
egg1 size = size1;
egg2 size = size2;
egg3 size = size3;
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("StackedEggsWriter");
my frame.setSize(frame width, frame height);
my frame.setVisible(true);
}

/** paintComponent fills the window with the eggs


* @param g - the graphics pen */
public void paintComponent(Graphics g)
{ g.setColor(Color.yellow);
g.fillRect(0, 0, frame width, frame height); // paint the background
// lay the first egg at the bottom of the frame:
int egg1 top = paintAnEgg(frame height, egg1 size, g);
// stack the two remaining eggs on top of it:
int egg2 top = paintAnEgg(egg1 top, egg2 size, g);
int egg3 top = paintAnEgg(egg2 top, egg3 size, g);
}
...
198

Figure 5.9: repeated invocations of a private method (concl.)


/** paintAnEgg paints an egg in 3-by-2 proportion (the egg’s height
* is two-thirds of its width)
* @param bottom - the position of the egg’s bottom
* @param width - the egg’s width
* @param pen - the graphics pen that will draw the egg
* @return the y-position of the painted egg’s top edge. */
private int paintAnEgg(int bottom, int width, Graphics pen)
{ int height = (2 * width) / 3;
int top edge = bottom - height;
int left edge = (frame width - width) / 2;
pen.setColor(Color.pink);
pen.fillOval(left edge, top edge, width, height);
pen.setColor(Color.black);
pen.drawOval(left edge, top edge, width, height);
return top edge;
}

/** Test the window: */


public static void main(String[] args)
{ int total width = 300;
int total height = 200;
new StackedEggsWriter(total width, total height, 50, 90, 140);
}
}
5.6. PRIVATE METHODS 199

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());

s = JOptionPane.showInputDialog("Type size of middle egg (an int):");


writer.setEggSize2(new Integer(s).intValue());

s = JOptionPane.showInputDialog("Type size of top egg (an int):");


writer.setEggSize3(new Integer(s).intValue());
}
}

3. Modify the paintBorder method in Figure 8 so that it paints a white-filled circle


with diameter equal to the window’s height in the center of the window in front
of a yellow background.

4. This class can benefit from a private method; insert it.

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);
}

public void paintComponent(Graphics g)


{ int x_pos = 100; // x-position of center of circle
int y_pos = 100; // y-position of center of circle
int diameter = 60; // diameter of circle to draw
g.setColor(Color.black);
int radius = diameter / 2;
g.drawOval(x_pos - radius, y_pos - radius, diameter, diameter);
diameter = diameter + 20;
radius = diameter / 2;
g.drawOval(x_pos - radius, y_pos - radius, diameter, diameter);
diameter = diameter + 20;
200

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):

/** printLadybug prints a ladybug */


public void printLadybug()
{ System.out.println(" ‘m\’"); // the ’ must be written as \’
System.out.println(" (|) sahr");
System.out.println();
}

• formal parameter (from Figure 3):

/** printBeeWithName prints a bee with a name attached to its stinger.


* @param name - the name attached to the stinger */
public void printBeeWithName(String name)
{ System.out.println(" ,-.");
System.out.println(" \\_/");
System.out.println(">{|||}-" + name + "-");
System.out.println(" / \\");
System.out.println(" ‘-^ hjw");
System.out.println();
}

• function (from Figure 7):

/** celsiusIntoFahrenheit translates degrees Celsius into Fahrenheit


* @param c - the degrees in Celsius
* @return the equivalent degrees in Fahrenheit */
public double celsiusIntoFahrenheit(double c)
{ double f = ((9.0 / 5.0) * c) + 32;
return f;
}
5.7. SUMMARY 201

• private method (from Figure 9):

/** paintAnEgg paints an egg in 3-by-2 proportion (the egg’s height


* is two-thirds of its width)
* @param bottom - the position of the egg’s bottom
* @param width - the egg’s width
* @param pen - the graphics pen that will draw the egg
* @return the y-position of the painted egg’s top edge. */
private int paintAnEgg(int bottom, int width, Graphics pen)
{ int height = (2 * width) / 3;
int top_edge = bottom - height;
int left_edge = (FRAME_WIDTH - width) / 2;
pen.setColor(Color.pink);
pen.fillOval(left_edge, top_edge, width, height);
pen.setColor(Color.black);
pen.drawOval(left_edge, top_edge, width, height);
return top_edge;
}

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();
} )

• invoking a method: sending a message that includes a method’s name (e.g.,


writer.printBee()). The message requests that the receiver object (writer)
execute the method named in the message (printBee).

• client: the object that sends a message (an invocation).

• 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

• result of a method: an “answer” calculated by a method and given back to its


client (e.g., return f in the body of celsiusToFahrenheit in Figure 7.
• function: a method that returns a result
• scope of a formal parameter: the statements where the parameter can be refer-
enced and assigned. This is normally the method’s body where the parameter
is defined (e.g., the scope of formal parameter s of writeSentence consists of
the two statements, sentence = s; this.repaint();).
• (interface) specification: a list of a class’s methods and their behaviors; can
also list the class’s private attributes (e.g., Table 5 is the specification for class
MyWriter in Figure 6).

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.

5.8 Programming Projects


1. Return to the Programming Projects for Chapter 4. For any of the projects that
you completed, revise the application so that it displays its output information
in a graphics window that you designed first with a specification and then coded.
For example, if you built the application that helps a child learn multiplications,
then the application might display an input view that asks,

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

application asks 5 voters to vote for either Candidate 1 or Candidate 2. The


application finishes by displaying the candidates’ names, addresses, and total
vote counts.
3. Answers have more impact when displayed visually. Write an application that
displays the distances one can travel on a full tank of gasoline at different
velocities. Use this formula to calculate distance travelled for a positive velocity,
v:
distance = (40 + 0.05v − (0.06v)2 ) ∗ capacity
where capacity is the size of the automobile’s fuel tank. (Note: This simplistic
formula assumes that the car has a one-gear transmission.)
The input to the application is the fuel tank’s size; the output are the distances
travelled (and the time taken to do so) for velocities 20, 40, 60, 80, and 100 miles
per hour. Display the answers graphically, so that the answers are pictures like
this:
For a 10-gallon fuel tank:
/A CAR\
40 m.p.h.: =================== -o--o-- 362.4 miles in 9.06 hours

/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

to tell it that one US Dollar can buy 0.9428 of one Euro.


Once it is started, the application asks the user to type the amount of currency
the user wishes to sell. When the user submits this information, the application
computes the amount of currency purchased and displays the answer as two
proportionally sized coins:
5.8. PROGRAMMING PROJECTS 205

IMAGE OF TWO BALLOONS, LABELLED AS: 200 Dollars = 188.56 Euros

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:

The class should have these methods:


206

class BarGraphWriter helps a user draw bar graphs


Methods
setAxes(int x pos, int draw the x- and y-axes of the graph. The pair,
y pos, String top label, x pos, y pos, state the coordinates on the win-
int y height) dow where the two axes begin. The height of
the y-axis, stated in pixels, is y height. (The
length of the x-axis will be exactly long enough
to display 6 bars.) The label placed at the top
of the y-axis is top label. (The label placed at
the bottom of the y axis is always 0.) See the
picture above.
setBar1(String label, int draw the first bar in the graph, where the label
height, Color c) underneath the bar is label, the height of the
bar, in pixels, is height, and the bar’s color is
c. See the picture above.
setBar2(String label, int draw the second bar in the graph, where the ar-
height, Color c) guments are used in the same way as in setBar1
setBar3(String label, draw the third through sixth bars of the graph
int height, Color c),
setBar4(String label,
int height, Color c),
setBar5(String label,
int height, Color c),
setBar6(String label, int
height, Color c)

(a) Here is the application that drew the above graph:


import java.awt.*;
public class TestGraph
{ public static void main(String[] a)
{ BarGraphWriter e = new BarGraphWriter();
e.setTitle("Days in first four months of the year");
e.setAxes(20, 120, "30", 90); // x- and y-axes start at 20, 120
// graph is 90 pixels high; top of graph is labelled "30"
int scale_factor = 3;
e.setBar1("Jan", 31 * scale_factor, Color.red); // Jan has 31 days
e.setBar2("Feb", 28 * scale_factor, Color.white); // etc.
e.setBar3("Mar", 31 * scale_factor, Color.blue);
e.setBar4("Apr", 30 * scale_factor, Color.red);
}
}
Test your coding of class BarGraphWriter with TestGraph.
5.8. PROGRAMMING PROJECTS 207

(b) Here is table of interesting statistics about the first six planets:

Mercury Venus Earth Mars Jupiter Saturn


Distance 0.387 0.723 1.00 1.524 5.203 9.539
from Sun
(astro-
nomical
units)
Mass 0.05 0.81 1.00 0.11 318.4 95.3
(rela-
tive to
Earth)
Length 2106.12 718 23.93 24.62 9.83 10.03
of day
(hours)
Length 88.0 days 224.7 365.3 687 days 11.86 29.46
of year days days years years
(in
Earth
days)
Weight 0.25 0.85 1.00 0.36 2.64 1.17
of 1kg.
object
Plot some or all of these statistics in bar graphs.
(c) Alter the MakeChange application in Figure 3, Chapter 3, so that it reads
its input interactively and displays the amounts of change as a four-bar
graph. (Think of these as “stacks” of coins!)
(d) Write an application that helps the user plot a bar graph of her own.
The application first asks the user for the title of the graph. Next, the
application asks for the largest value that will be plotted as a bar—this
will be used as the label on the graph’s y-axis. Then, the application asks
the user for the values of the six bars to be drawn. (If the user does not
want one of the six bars to appear, she types 0 for its value and newline
by itself for the bar’s label.) The application displays the bar graph as its
answer.
Notice that the application decides for itself the location of the x- and
y-axes. Here is how the interaction between the application and its user
might go (the user’s responses are stated in italics):
Please type the title of your graph: <em>Days in the months</em>
Please type the value of the largest bar you will draw: <em>31</em>
208

Please type the name of the first bar: <em>Jan</em>


Please type the value of the first bar: <em>31</em>
... etc. ...

7. Another form of graph is a sequence of plotted points, connected by lines. Here


is a plot of the equation, y = x*x, where the values of y for x equals 0,1,...,5 are
plotted and connected:

Write a class, PointGraphWriter, that generates a graph of exactly 6 plotted


points. The class should meet this specification:
5.8. PROGRAMMING PROJECTS 209

class PointGraphWriter helps a user draw a graph of 6 plotted points


Methods
setAxes(int x pos, int draw the the vertical and horizontal axes of the
y pos, int axis length, graph, such that the intersection point of the
String x label, String axes lies at position x pos, y pos. Each axis has
y label) the length, axis length pixels. The beginning
labels of both the x- and y-axes are 0; the label
at the top of the y-axis is y label, and the label
at the end of the x-axis is x label.
setPoint1(int height) plot the first point of the graph, so that it ap-
pears at the 0-position on the x-axis and at the
height position on the y-axis.
setPoint2(int height) plot the second point of the graph, so that its x-
position is at one-fifth of the length of the x-axis
and at the height position on the y-axis.
setPoint3(int height), plot the third and subsequent points of the
setPoint4(int height), graph, so that they are equally spaced along
setPoint5(int height), the x-axis and appear at the specified height
setPoint6(int height) on the y-axis. Each point is connected to its
predecessor by a straight line.

Test the class you write on these applications:


(a) the graph seen above. Here is the application that does this; test your
coding of class PointGraphWriter with it.
public class TestPlot
{
public static void main(String[] a)
{ PointGraphWriter e = new PointGraphWriter();
e.setTitle("Graph of y = x*x");
e.setAxes(50, 110, 90, "5", "30");
// axes start at position 50,110; the axes have length 90 pixels
// x-axis is labelled 0..5
// y-axis is labelled 0..30
int scale_factor = 3;
e.setPoint1(0 * scale_factor); // 0*0 = 0
e.setPoint2(1 * scale_factor); // 1*1 = 1
e.setPoint3(4 * scale_factor); // 2*2 = 4
e.setPoint4(9 * scale_factor); // etc.
e.setPoint5(16 * scale_factor);
e.setPoint6(25 * scale_factor);
}
}
210

(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.

8. Here is an example of a “pie chart”:

The chart was generated from an output-view object with this specification:
5.8. PROGRAMMING PROJECTS 211

class PieChartWriter helps a user draw a pie chart of at most 6


“slices”
Methods
setSlice1(String label, int draw the first slice of the chart, such that amount
amount, Color c) indicates the amount of the slice, and c is the
slice’s color. The label is printed to the right
of the pie, and it is printed in the color, c.
setSlice2(String label, draw the subsequent slices and their labels.
int amount, Color c),
setSlice3(String label,
int amount, Color c),
setSlice4(String label,
int amount, Color c),
setSlice5(String label,
int amount, Color c),
setSlice6(String label,
int amount, Color c)

Use the class to plot


(a) the chart seen above. Here is the application that does this; test your
coding of class PieChartWriter with it.
import java.awt.*;
public class TestPieChart
{ public static void main(String[] args)
{ PieChartWriter p = new PieChartWriter();
p.setTitle("How I spend my day");
p.setSlice1("Sleep: 7 hours", 7, Color.black);
p.setSlice4("Recreation: 9 hours", 9, Color.gray);
p.setSlice2("In class: 3 hours", 3, Color.blue);
p.setSlice3("Homework: 5 hours", 5, Color.red);
}
}

(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

5.9 Beyond the Basics


5.9.1 Naming Variables, Methods, and Classes

5.9.2 Generating Web Documentation with javadoc

5.9.3 Static Methods

5.9.4 How the Java Compiler Checks Typing of Methods

5.9.5 Formal Description of Methods

5.9.6 Revised Syntax and Semantics of Classes

These optional sections build on the core materials in the chapter and show

• how to invent names for methods, classes, parameters, and variables

• how to use the javadoc program to create API documentation for the classes
you write

• how the main method can use static private methods

• how to state precisely the syntax and semantics of method declaration and in-
vocation

5.9.1 Naming Variables, Methods, and Classes


Here are the guidelines used in this text for devising names for variables, methods,
and classes. The guidelines are devised so that we can read a name and deduce almost
immediately whether it is a name of a variable, a method, or a class.
5.9. BEYOND THE BASICS 213

• 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).

• The names of formal parameters and variables should be should be descriptive


but succinct. In this text we use all lower-case letters for variables, where the the
underscore character separates individual words in a variable name, e.g., answer
and left edge. This distinguishes variable names from method and class names.
A commonly used alternative is to use the same spelling for method names as
for variable names—do as you please.

• 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

5.9.2 Generating Web Documentation with javadoc

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

encounter a summary of the class’s public methods:

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

jump to the link named writeSentence and view the following:

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.

One-line comments of the form, // ..., are ignored by javadoc.


Important: javadoc will not normally include commentary about private fields
and methods in its web pages, because these components are not meant for public
use. If you desire documentation of a class’s private components, you obtain this by
using the -private option, e.g., javadoc -private TemperaturesWriter.java.
javadoc is best used to document a package of classes; we organize applications
into packages in in Chapter 9.

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.

5.9.3 Static Methods


An application is typically designed as a collection of classes, where each class contains
methods and private field variables. A class’s methods and fields work together to
perform the class’s responsibilities—a good example is class MyWriter in Figure 6.
218

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

public class CelsiusToFahrenheit2


{ public static void main(String[] args)
{ String input =
JOptionPane.showInputDialog("Type an integer Celsius temperature:");
int c = new Integer(input).intValue(); // convert input into an int
double f = celsiusIntoFahrenheit(c);
DecimalFormat formatter = new DecimalFormat("0.0");
System.out.println("For Celsius degrees " + c + ",");
System.out.println("Degrees Fahrenheit = " + formatter.format(f));
}

/** celsiusIntoFahrenheit translates degrees Celsius into Fahrenheit


* @param c - the degrees in Celsius
* @return the equivalent degrees in Fahrenheit */
private static double celsiusIntoFahrenheit(int c)
{ return ((9.0 / 5.0) * c) + 32; }
}

The private method, celsiusIntoFahrenheit, must itself be labelled static, because


the private method is itself invoked without explicitly constructing an object that
holds it. As a rule, all methods invoked by a static method must themselves be
labelled static.
There is another use of static methods in Java: We might argue that some
methods, like the square-root, exponentiation, sine, and cosine methods, really do
not need to be held within an explicitly constructed object. Of course, one might also
argue that it is reasonable to construct an object that has the abilities to compute
precisely these operations!
The Java designers followed the first argument, and they wrote class Math, which
resides in the package java.lang. Inside class Math are the static methods, sqrt,
pow, sin, and cos, mentioned above. Because these methods are labelled static, we
invoke them differently than usual: Rather than constructing a new Math() object
and sending sqrt messages to it, we invoke the method directly:

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.

5.9.4 How the Java Compiler Checks Typing of Methods


The data typing information in a method’s header line helps the Java compiler validate
both the well formedness of the method’s body as well as the correctness of invocations
of the method. When the compiler reads a method like
public double celsiusIntoFahrenheit(double c)
{ double f = ((9.0 / 5.0) * c) + 32;
return f;
}

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.

5.9.5 Formal Description of Methods


Because method writing is fundamental to programming, here is a formal description
of the syntax and semantics of method definition and invocation. The steps behind
type checking and executing method invocations are surprisingly intricate, mainly
because Java allows one class to extend (inherit) another’s structure.
The material that follows is provided as a reference and most definitely is nonre-
quired reading for the beginner.

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

Recall that ? means that the preceding phrase is optional.


VISIBILITY ::= public | private
RETURN_TYPE ::= TYPE | void
METHOD_HEADER ::= IDENTIFIER ( FORMALPARAM_LIST? )

A method can be public or private and can optionally return a result. The syntax of
TYPE remains the same from Chapter 3.

FORMALPARAM_LIST ::= FORMALPARAM [[ , FORMALPARAM ]]*


FORMALPARAM ::= TYPE IDENTIFIER
METHOD_BODY ::= { STATEMENT* }

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

RETURN ::= return EXPRESSION ;

A method definition is well typed if

• 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:

– each formal parameter, TYPE IDENTIFIER, defines a variable, IDENTIFIER of


type TYPE.
– each field, private TYPE IDENTIFIER, in the class in which this method
appears defines a variable, IDENTIFIER of type TYPE.

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:

INVOCATION ::= [[ RECEIVER . ]]? IDENTIFIER ( ARGUMENT_LIST? )

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:

[[ RECEIVER . ]]? NAME0 ( EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn)

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:

VISIBILITY TYPE0 NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)

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:

[[ RECEIVER . ]]? NAME0 (EXPRESSION1: TYPE1, EXPRESSION2: TYPE2,


..., EXPRESSIONn:TYPEn) public;

When NAME0 is a private method, the invocation looks like this:

[[ RECEIVER . ]]? NAME0 (EXPRESSION1: TYPE1, EXPRESSION2: TYPE2,


..., EXPRESSIONn:TYPEn) private Ck;

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);
}

private static String setTitle(int i)


{ return ("New " + i); }
}
224

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;

and it notes that the result will be a String, which is acceptable.


Next, the compiler analyzes w.setTitle(s). The prefix, w, has type FourEggsWriter,
so that class is searched for a public method named setTitle. None is found,
but since class FourEggsWriter extends JFrame, the compiler searches class JFrame
and finds the best matching method there, JFrame’s setTitle. The invocation is an-
notated as
w.setTitle(s: String) public;

Definition of Best Matching Method


As the two previous examples indicate, the “best matching method” for an invocation
is the method, NAME0, whose n formal parameters have data types that match the
data types of the n actual parameters. But we should state this more precisely, if
only because the concept is extended in Chapter 9.
Here is a more precise definition:
Again, let
[[ RECEIVER . ]]? NAME0 ( EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn)

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,

NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)

such that

• There are exactly n formal parameters to match the n actual parameters.


5.9. BEYOND THE BASICS 225

• The data type of each actual parameter, EXPRESSIONi, is a (sub)type of


TYPEi, the data type of the corresponding formal parameter, NAMEi.

If the method is found, the search is successful.


2. If there is no such method, NAME0, that fits these criteria, 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.
We return to the earlier example; consider
w.setTitle(s);

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.

Executing Method Invocations


Finally, we consider the execution semantics of method invocation. Thanks to the
compiler’s annotations, the invocation has this form:
[[ RECEIVER ]]? NAME0(EXPRESSION1: TYPE1, EXPRESSION2:TYPE2,
..., EXPRESSIONn:TYPEn) SUFFIX

(Recall that SUFFIX is either private Ck or public.) For simplicity, we consider


invocations of only normal (non-static) methods.
Execution proceeds in the following steps:
1. Calculate the address of the receiver object: Compute RECEIVER to get the ad-
dress of an object. (If RECEIVER is absent, use the address of the object in which
this invocation is executing.) Call the address, a. Let C0 be the run-time data
type that is attached to the object at address a.
2. Select the method in the receiver:
• If the SUFFIX is private Ck, select the private method from class Ck,
whose header line has the form:
226

private ... NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)

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.

3. Evaluate the actual parameters: Each of EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn


is evaluated in turn to its result. Call the results r1, r2, ..., rn.

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:

{ TYPE1 NAME1 = r1;


TYPE2 NAME2 = r2;
...
TYPEn NAMEn = rn;
BODY
}

Step 2 is necessary so that there is no confusion when a programmer uses the


same name, p, for a private method in one class and a public method in another
class. Also, the “searching” that one does to select a public method is necessary to
resolve overridden methods. (For example, class JFrame has a useless paint method,
but in Figure 2 class FourEggsWriter has a newer paint method. When we create
a FourEggsWriter object, it has two paint methods, but the newer one is always
selected.)
5.9. BEYOND THE BASICS 227

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.

5.9.6 Revised Syntax and Semantics of Classes


Classes now have fields and constructor methods, so here are the augmentations to
the “Syntax” and “Semantics” sections in Chapter 2.
The syntax of a class becomes
public class IDENTIFIER EXTEND?
{ FIELD*
CONSTRUCTOR
METHOD*
}
EXTEND ::= extends IDENTIFIER
FIELD ::= private DECLARATION
CONSTRUCTOR ::= public METHOD_HEADER
{ STATEMENT* }
Data-type checking is performed on the fields, constructor method, and methods of
the class. No two fields may be declared with the same name. Two methods may be
declared with the same name if they have different quantities or data types of formal
parameters; this issue is explored in Chapter 9.
The semantics of a class is that it is remembered for creating objects.
An object is constructed from a class, class C0, by an expression of the form
new C0( EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn )
The semantics of object construction goes as follows:
1. Construct an object in computer storage: class C0 is located, and copy of its
components are allocated in storage. If C0 extends another class, C1, then C1’s
components are copied into the object as well.
228

2. Execute all the field declarations.

3. Execute the constructor method: The phrase, C0(EXPRESSION1, EXPRESSION2,


..., EXPRESSIONn), is executed like a method invocation, as described in the
previous section.

4. Remove all constructor methods from the object—they are no longer needed.

5. Return as the result the address of the object.


Chapter 6

Control Structure: Conditional State-


ments and Software Architecture
6.1 Control Flow and Control Structure

6.2 Conditional Control Structure

6.2.1 Nested Conditional Statements

6.2.2 Syntax Problems with Conditionals

6.3 Relational Operations

6.4 Uses of Conditionals

6.5 Altering Control Flow

6.5.1 Exceptions

6.5.2 System Exit

6.5.3 Premature Method Returns

6.6 The Switch Statement

6.7 Model and Controller Components

6.8 Case Study: Bank Accounts Manager

6.8.1 Collecting Use-Case Behaviors

6.8.2 Selecting a Software Architecture

6.8.3 Specifying the Model

6.8.4 Writing and Testing the Model

6.8.5 Specifying the View Components

6.8.6 A Second Look at the Software Architecture

6.8.7 Writing the View Classes

6.8.8 Controller Construction and Testing

6.8.9 Testing the Assembled Application

6.8.10 Multiple Objects from the Same Class


230

6.9 More about Testing Classes and Methods

6.9.1 Testing Individual Methods

6.9.2 Testing Methods and Attributes Together

6.9.3 Testing a Suite of Methods

6.9.4 Execution Traces

6.10 Summary

6.11 Programming Projects

6.12 Beyond the Basics

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.

• We use our knowledge of control flow to develop application building in more


detail, so that the flow of control between components is clear. Specifically,
we divide an application into three subassemblies: model, view, and controller,
where the view takes resposibility for data input and output, the controller takes
responsibility for control flow, and the new component, the model, takes respon-
sibility for computing answers.

This Chapter complements the previous chapter by showing that control and compo-
nent structure are complementary and equally important to program development.

6.1 Control Flow and Control Structure


The order in which a program’s statements execute is called its control flow. Of course,
by writing statements in a sequential order and inserting method invocations, a pro-
grammer specifies a program’s control flow. When a programmer writes statements
in a sequential ordering,
6.2. CONDITIONAL CONTROL STRUCTURE 231

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.

6.2 Conditional Control Structure


Although we have accomplished an impressive amount of class building, we must
make our classes’ methods more “intelligent.” Specifically, we wish to enable our
methods to ask questions about the arguments they receive so that the methods can
choose a control flow best suited to the arguments’ values.
Real-life algorithms often contain instructions that take a course of action based
on the answer to a question, for example,

• 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:

/** printPolarity prints whether its argument is negative or not.


* @param i - the argument */
public void printPolarity(int i)
{ System.out.print("The argument is ");
if ( i < 0 )
{ System.out.println("negative."); }
else { System.out.println("nonnegative."); }
System.out.println("Finished!");
}

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

The argument is nonnegative.


Finished!

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

The argument is negative.


Finished!

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 }

which is an abbreviation of if ( TEST ) { STATEMENTS } else { }.


An arm of a conditional statement can hold multiple statements, as we see in the
application in Figure 1, which translates hours into seconds. The algorithm upon
which the application is based is simple but significant, because it asks a question:

1. Read an integer, representing a quantity of hours, from the user.

2. If the integer is nonnegative, then translate the hours into seconds.


Else, the integer is not nonnegative (that is, it is negative), so display an error
message.

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

Figure 6.1: application that converts hours into seconds


import javax.swing.*;
/** ConvertHours translates a time in hours into its equivalent in seconds.
* Input: a nonnegative integer
* Output: the converted time into seconds. */
public class ConvertHours
{ public static void main(String[] args)
{ int hours = new Integer(
JOptionPane.showInputDialog("Type hours, an integer:")).intValue();
if ( hours >= 0 )
{ // at this point, hours is nonnegative, so compute seconds:
int seconds = hours * 60 * 60;
JOptionPane.showMessageDialog(null,
hours + " hours is " + seconds + " seconds");
}
else { // at this point, hours must be negative, an error:
JOptionPane.showMessageDialog(null,
"ConvertHours error: negative input " + hours);
}
}
}

Exercises
1. What will these examples print?

(a) double d = 4.14;


int i = 3;
if ( i == d )
{ System.out.println("Equal"); }
else { i = (int)d; }
if ( i != 3 )
{ System.out.println("Not equal"); }
(b) public static void main(String[] args)
{ System.out.println( f(2, 3) );
int i = 2;
System.out.println( f(i+1, i) );
}

private static String f(int x, int y)


{ String answer;
if ( x <= y )
{ answer = "less"; }
6.2. CONDITIONAL CONTROL STRUCTURE 235

else { answer = "not less"; }


return answer;
}

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 {}

3. Use conditional statements to write the bodies of these methods:

(a) /** printEven prints whether its argument is even or odd:


* It prints "EVEN" when the argument is even.
* It prints "ODD" when the argument is odd.
* @param a - the argument */
public void printEven(int a)
(b) /** minus subtracts its second argument from its first, provided that
* the result is nonnegative
* @param arg1 - the first argument, must be nonnegative
* @param arg2 - the second argument
* @return (arg1 - arg2), if arg1 >= arg2;
* return -1, if arg1 is negative or if arg2 > arg1 */
public int minus(int arg1, int arg2)
(Hint: place a conditional statement inside a conditional.)
(c) /** div does integer division on its arguments
* @param arg1 - the dividend
* @param arg2 - the divisor
* @return (arg1 / arg2), provided that arg2 is nonzero;
* return 0 and print an error message, if arg2 == 0 */
public int div(int arg1, int arg2)

6.2.1 Nested Conditional Statements


Conditionals can nest within conditional statements. This can be useful when there
are multiple questions to ask and asking one question makes sense only after other
questions have been asked. Here is a simple example.
Perhaps we revise the change-making application from Chapter 3 so that it verifies
that the dollars and cents inputs are reasonable: the dollars should be nonnegative
236

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:

1. Read the dollars, an integer

2. If dollars are negative, then generate an error message.


Else dollars are nonnegative:

(a) If cents are negative, then generate an error message.


Else cents are nonnegative:
i. If cents are greater than 99, then generate an error message.
Else cents are in the range 0..99:
A. Compute the total of the dollars and cents
B. Extract the appropriate amount of quarter, dime, nickel, and penny
coins.

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

Figure 6.2: change-making with nested conditionals


import javax.swing.*;
/** MakeChangeAgain calculates change in coins for a dollars, cents amount.
* Input: two numbers supplied at the command line:
* a dollars amount, a nonnegative integer;
* a cents amount, an integer between 0 and 99.
* Output: the coins */
public class MakeChangeAgain
{ public static void main(String[] args)
{ int dollars = new Integer(args[0]).intValue();
if ( dollars < 0 )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: negative dollars: " + dollars);
}
else // dollars amount is acceptable, so process cents amount:
{ int cents = new Integer(args[1]).intValue();
if ( cents < 0 )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: negative cents: " + cents);
}
else { if ( cents > 99 )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: bad cents: " + cents);
}
else // dollars and cents are acceptable, so compute answer:
{ int money = (dollars * 100) + cents;
System.out.println("quarters = " + (money / 25));
money = money % 25;
System.out.println("dimes = " + (money / 10));
money = money % 10;
System.out.println("nickels = " + (money / 5));
money = money % 5;
System.out.println("pennies = " + money);
}
}
}
}
}
238

Figure 6.3: flowchart for change making

read dollars;
dollars negative?

error read cents;


cents negative?

error cents > 99?

error total dollars and cents;


extract coins from total

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.

3. (a) Write this method; use nested conditionals


/** convertToSeconds converts an hours, minutes amount into the
* equivalent time in seconds.
* @param hours - the hours, a nonnegative integer
* @param minutes - the minutes, an integer in the range 0..59
* @return the time in seconds; in case of bad arguments, return -1 */
public int convertToSeconds(int hours, int minutes)
6.2. CONDITIONAL CONTROL STRUCTURE 239

Figure 6.4: unnesting conditional statements


import javax.swing.*;
/** MakeChangeAgain calculates change in coins for a dollars, cents amount.
* Input: two numbers supplied at the command line:
* a dollars amount, a nonnegative integer;
* a cents amount, an integer between 0 and 99.
* Output: the coins */
public class MakeChangeAgain2
{ public static void main(String[] args)
{ boolean ok = true; // remembers whether input data is acceptable
int dollars = new Integer(args[0]).intValue();
int cents = new Integer(args[1]).intValue();
if ( dollars < 0 )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: negative dollars: " + dollars);
ok = false; // the error negates acceptability
}
if ( ok )
// dollars are acceptable, so consider cents:
{ if ( cents < 0 )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: negative cents: " + cents);
ok = false;
}
if ( cents > 99 )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: bad cents: " + cents);
ok = false;
}
}
if ( ok )
// dollars and cents are acceptable, so compute answer:
{ int money = (dollars * 100) + cents;
System.out.println("quarters = " + (money / 25));
money = money % 25;
System.out.println("dimes = " + (money / 10));
money = money % 10;
System.out.println("nickels = " + (money / 5));
money = money % 5;
System.out.println("pennies = " + money);
}
}
}
240

(b) Next, rewrite the previous method with no nested conditionals—use a


boolean variable to remember the status of the conversion.

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.

6.2.2 Syntax Problems with Conditionals


Java’s form of conditional statement has a problematic syntax that allows serious
errors to go undetected by the Java compiler.
For example, when an arm of a conditional is a single statement, it is legal to omit
the brackets that enclose the arm. This can prove disastrous—say that we have this
conversion method, and we forget the brackets around the else-arm:
public int convertHoursIntoSeconds(int hours)
{ int seconds;
if ( hours >= 0 )
{ int seconds = hours * 60 * 60; }
else JOptionPane.showMessageDialog(null, "error: returning 0 as answer");
seconds = 0;
return seconds;
}
Because of the missing brackets, the Java compiler decides that the else-arm is just
one statement, and the compiler inserts this bracketing:
if ( hours >= 0 )
{ int seconds = hours * 60 * 60; }
else { JOptionPane.showMessageDialog(null, "error: returning 0 as answer"); }
seconds = 0;
which is certainly not what was intended.
Just as bad is an accidental omission of the keyword, else. The missing else in
the same example,
if ( hours >= 0 )
{ int seconds = hours * 60 * 60; }
{ JOptionPane.showMessageDialog(null, "error: returning 0 as answer");
seconds = 0;
}
is not noticed by the compiler, which assumes that the conditional has only a then-
arm. As a result, the statements following the then-arm are always executed, which
is again incorrect.
Another problem that can arise when brackets are absent is the famous “dangling
else” example. Consider this example, deliberately badly formatted to reveal no
secrets:
6.3. RELATIONAL OPERATIONS 241

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:

if ( minute < 10 ) { } else { }


{ answer = answer + "0"; }

This makes the conditional worthless.


Once you are certain your conditional statement is correctly written, you should
test it. Remember that a conditional statement contains two possible paths of execu-
tion and therefore should be tested at least twice, with one test that causes execution
of the then-arm and one test that causes execution of the else-arm.

6.3 Relational Operations


In Chapter 3, we encountered comparison operations for writing boolean-typed ex-
pressions; for review, here are some samples:

• i >= 3 (i is less-than-or-equals 3)

• 2 < (x + 1.5) (2 is less than x plus 1.5)

• y != 2 (y does not equal 2)

• 5 == 3*2 (5 equals 3 times 2)


242

Such expressions can be used as tests within conditional statements.


Sometimes we wish to insert two comparison operations within one test expression.
For example, the change-making applications in Figures 2 and 4 would benefit from
asking, “cents < 0 or cents > 99”? as one question, rather than two.
To do so, we may use relational operations, which operate on boolean arguments
and produce boolean answers. For example, the logical disjunction, “or,” is written
in Java as the relational operation, ||, so we can state,
if ( (cents < 0) || cents > 99) )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: bad cents: " + cents);
...
}

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

Figure 6.5: logical operations

Operator Semantics
E1 && E2 conjunction (“and”):

true && true => true

true && false => false

false && E2 => false

E1 || E2 disjunction (“or”):

false || false => false

false || true => true

true || E2 => true

!E negation(“not”):

!true => false

!false => true

asks whether integer x is a prime less than 10, and


(y >= 0) && (((y % 3) == 0) || (y < 3))

asks whether integer y is nonnegative and is either a multiple of 3 or has value 0, 1,


or 2. Also, a negation operator always inverts the result produced by its argument,
e.g.,
!(cents >= 0 && cents <= 99)

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

Relational operations can be used to compress the nesting structure of condition-


als. Indeed, if we study the flowchart in Figure 3, we notice that the three tests are
244

Figure 6.6: change making with logical relational operations


import javax.swing.*;
/** MakeChangeAgain calculates change in coins for a dollars, cents amount.
* Input: two numbers supplied at the command line:
* a dollars amount, a nonnegative integer;
* a cents amount, an integer between 0 and 99.
* Output: the coins */
public class MakeChangeAgain3
{ public static void main(String[] args)
{ int dollars = new Integer(args[0]).intValue();
int cents = new Integer(args[1]).intValue();
if ( (dollars < 0) || (cents < 0) || (cents > 99) )
{ JOptionPane.showMessageDialog(null,
"MakeChangeAgain error: bad inputs: " + dollars + " " + cents);
}
else // dollars and cents are acceptable, so compute answer:
{ int money = (dollars * 100) + cents;
System.out.println("quarters = " + (money / 25));
money = money % 25;
System.out.println("dimes = " + (money / 10));
money = money % 10;
System.out.println("nickels = " + (money / 5));
money = money % 5;
System.out.println("pennies = " + money);
}
}
}

used to determine whether to generate an error dialog or to compute change. We


might be so bold as to combine all three tests into one with relational operations;
see Figure 6 for the result, where less precise error reporting is traded for a simpler
control structure.
Relational operations are evaluated after arithmetic operations and comparison
operations, and for this reason, it is acceptable to omit some of the parentheses in
the above example:

if ( dollars < 0 || cents < 0 || cents > 99 )

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

(a) (x > 1) && ((2*x) <= y)


(b) !(x == 1)
(c) (x >= 0 && x <= 1) || (1 <= y)
(d) x > 0 && x < 10 && (y == 3)

2. Given this method,

public int minus(int arg1, int arg2)


{ int answer;
if ( arg1 < 0 || arg2 > arg1 )
{ answer = -1; }
else { answer = arg1 - arg2; }
return answer;
}

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)

6.4 Uses of Conditionals


Two main uses of conditionals are

• 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

Here is an example that employs both these uses.


We desire a method, twelveHourClock, which converts a time from a twenty-
four hour clock into the corresponding value on a twelve-hour clock. (For example,
twelveHourClock(16,35) would return "4:35 p.m." as its result.) Such a method
must be intelligent enough to understand “a.m.” and “p.m.” as well as defend itself
against nonsensical arguments (e.g., twelveHourClock(-10,99)).
The algorithm for the method might go as follows:

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.

2. Return 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

Figure 6.7: method that uses conditionals to convert time


/** twelveHourClock converts a 24-hour-clock time into a 12-hour-clock time.
* @param hour - the hour time, in the range 0..23
* @param minute - the minutes time, in the range 0..59
* @return the 12-hour-clock time, formatted as a string. */
public String twelveHourClock(int hour, int minute)
{ String answer = ""; // accumulates the formatted time, symbol by symbol
if ( hour < 0 || hour > 23 || minute < 0 || minute > 59 )
{ answer = "twelveHourClock error: " + hour + "." + minute; }
else { if ( hour >= 13 ) // hours 13..23 are reduced by 12
{ answer = answer + (hour - 12); }
else { if ( hour == 0 ) // hour 0 is written as 12
{ answer = answer + "12"; }
else { answer = answer + hour; }
}
answer = answer + ":";
if ( minute < 10 ) // display minutes 0..9 as 00..09
{ answer = answer + "0"; }
answer = answer + minute;
if ( hour < 12 ) // morning or afternoon?
{ answer = answer + " a.m."; }
else { answer = answer + " p.m."; }
}
return answer;
}

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

String input = JOptionPane.showInputDialog


("Type some input and press a button:");
if ( input == null ) // did the user press CANCEL?
{ ... take appropriate action to cancel the transaction ... }
else { ... process the input ... }

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.

2. If the user pressed Cancel, then quit. Otherwise:

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.

5. Invoke twelveHourClock to convert the time and display it.

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:

public static void main(String[] args)


{ TimeConvertor time = new TimeConvertor(); // holds the method in Figure 7
String input = JOptionPane.showInputDialog
("Type hours in range 0..23:");
if ( input == null ) // did the user press Cancel?
{ JOptionPane.showMessageDialog(null, "Request cancelled"); }
else { int hours = new Integer(input).intValue();
int minutes = 0;
input = JOptionPane.showInputDialog
("Type minutes in range 0..59:");
if ( input != null ) // did the user press OK?
{ minutes = new Integer(input).intValue(); }
String answer = time.twelveHourClock(hours, minutes);
JOptionPane.showMessageDialog(null, "Time is " + answer);
}
}
6.5. ALTERING CONTROL FLOW 249

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:

/** translateGrade converts a numerical score to a letter grade.


* @param score - a numerical score that must be in the range 0..100
* @return a letter grade based on this scale:
* 100..90 = "A"; 89..80 = "B"; 79..70 = "C"; 69..60 = "D"; 59..0 = "F" */
public String translateGrade(int score)

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

6.5 Altering Control Flow


When we reason about conditional statements, we assume that there is a control flow
through the then-arm which reaches the end of the conditional and there is a control
flow through the else-arm which reaches the end of the conditional. But the Java
language allows a programmer to invalidate this assumption by altering the control
flow.
In the usual case, you should not alter the normal control flow of conditionals,
statement sequences, and method invocations—such alterations make it almost im-
possible for a programmer to calculate possible control flows and analyze a program’s
250

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;
}

Unfortunately, if this method is invoked with a negative actual parameter, it returns


an incorrect answer, meaning that the then-arm of the conditional has failed in its
goal of computing the conversion of hours into seconds. This is potentially disastrous,
6.5. ALTERING CONTROL FLOW 251

Figure 6.8: method with thrown exception


/** 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 )
{ String error = "convertHoursIntoSeconds error: bad hours " + hours;
JOptionPane.showMessageDialog(null, error);
throw new RuntimeException(error);
}
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:

throw new RuntimeException(ERROR_MESSAGE);

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.

6.5.2 System Exit


The java.lang package provides a method that, when invoked, immediately termi-
nates an application and all the objects constructed by it. The invocation is

System.exit(0);

This statement does not generate an error message on the display, and like throwing
exceptions, should be used sparingly.
252

6.5.3 Premature Method Returns


A less drastic means of terminating a method in the middle of its execution is to
insert a return statement. As noted in the previous chapter, a return statement
causes the flow of control to immediately return to the position of method invocation;
any remaining statements within the invoked method are ignored. Here are two small
examples. The first is a method that returns rather than continue with a dubious
computation:

public void printSeconds(int hours)


{ if ( hours < 0 )
{ return; }
int seconds = hours * 60 * 60;
System.out.println(hours + " is " + seconds + " seconds");
}

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:

public int convertToSeconds(int hours)


{ if ( hours < 0 )
{ return -1; }
int seconds = hours * 60 * 60;
return seconds;
}

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.

6.6 The Switch Statement


Say that you have written a nested conditional that asks questions about the value
of an integer variable, i and alters variable j:

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;
}
...

case VALUEn : { STATEMENTSn


break;
}
default : { STATEMENTSn+1 }
}

where EXPRESSION must compute to a value that is an integer or a character. (Recall


that Java characters are saved in storage as integer.) Each of VALUE1, VALUE2, ...,
254

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:

1. EXPRESSION is computed to its value, call it v.

2. If some VALUEk equals v, then the accompanying statements, STATEMENTSk, are


executed; if no VALUEk equals v, then statements STATEMENTSn+1 are executed.

A switch statement is often used to decode an input value that is a character.


Imagine that an application reads Celsius (’C’), Fahrenheit (’F’), Kelvin (’K’), and
Rankine (’R’) temperatures—it likely reads a character code, call it letter, that
denotes the temperature scale, and it is probably simplest to process the character
with a statement of the form,

switch ( letter )
{ case ’C’: { ...
break;
}
case ’F’: { ...
break;
}
case ’K’: { ...
break;
}
case ’R’: { ...
break;
}
default: { System.out.println("TempConverter error: bad code"); }
}

to process the possible temperature scales.


Unfortunately, Java’s switch statement has a primitive semantics that behaves
badly when a break statement is omitted—more than one case might be executed!
For this statement,

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

Figure 6.9: a simple model-view-controller architecture

CONTROLLER

INPUT VIEW
OUTPUT VIEW MODEL

when i holds 2, the control flow executes the statement, System.out.println("2"),


and because of the missing break, control “falls into” the next case and executes
System.out.println("5") and j = j + i as well! The break that follows sends the
control to the end of the switch.
An alternative to the switch statement is the following reformatted nested condi-
tional, where certain bracket pairs are carefully omitted and indentation is made to
look like that of the switch:

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.

6.7 Model and Controller Components


Virtually all the applications constructed so far in this text used a simple architecture,
the one seen in Figure 1, Chapter 4, where a controller asks an input-view for data,
computes answers from the data, and sends those answers to an output-view. It is
time to work with a more sophisticated architecture, of the form presented in Figure
9.
The controller decides the control flow of the application, the view(s) deal exclu-
sively with input and output transmission, and the new component, the model, does
the “modelling” of the problem and the “computing” of its answer. (In the Figure,
the arrows suggest that the the controller sends messages to the input-view, asking
for data; the controller then instructs the model to compute upon the data; and the
256

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.

3. The architecture isolates the program’s components so that alterations to one


part do not necessitate rewriting the other parts. In particular, one component
can be removed (“unplugged”) from the architecture and replaced by another,
which is “plugged” into the former’s place.

In the ideal situation, the building of an application should be a matter of

• selecting already written classes, perhaps extending them slightly, to generate


the input-output- iews

• selecting an already written class and perhaps extending it to generate the


model

• 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

existing class refers to inheritance, which we employed in previous chapters to build


a variety of graphics windows.
As noted earlier, a model has the responsibility for “modelling” and “computing”
the problem to be solved. This modelling is done with the methods and the field
variables declared within the model class. (We call the field variables the model’s
attributes or its internal state.)
For example, a bank-account manager program uses a model that models an ac-
count ledger—the model has field variables to remember the account’s balance, and
the model owns methods for depositing and withdrawing funds, computing interest,
and displaying the balance. A model for a chess game uses fields to model the
playing board and pieces, and the model possesses methods that move pieces and
compute interactions of pieces (e.g., “captures”). And, a model of a weather predic-
tion program models planet Earth by using field variables for land masses, air and
water currents, and by using methods that compute the interaction of the fields.
A model is somewhat like the “chip” or “board” from a hand-held calculator (or
other electronic appliance), because the model contains internal state, it contains
operations that use the state, but it does not contain the calculator’s input buttons
or display panel (the “views”) or the calculator’s wiring and switches that instruct
the chip what to do (the “controller”).
In contrast, an application’s controller takes responsibility for controlling the order
in which the computation proceeds. The controller uses the input data to determine
the appropriate control flow and to invoke the appropriate methods of the model to
produce the output to be displayed. We use the techniques learned in the first part
of the Chapter to write the controllers that appear in the sections to follow.

6.7.1 Designing an Application with a Model-View-Controller


Architecture
When someone asks us to design and build an application, we must gather infor-
mation about what the application must do so that we can design the application’s
architecture appropriately and build components that make the application come to
life.
To design and build an application, we should follow these five steps:
1. Collect use-case behaviors:
First, we ask the program’s user what the program is supposed to do; specifi-
cally, we ask the user how the program should behave in all of the cases when
the user pushes a button, types some input data, and so on. (For example, if
we are programming a text-editing program, we would ask the user to describe
all the desired behaviors of the program — entering text, pressing the “Save
File” button, pressing the “Undo” button, and so on. For each behavior, we
would list the program’s response.)
258

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.

2. Select a software architecture that can realize the use-case behaviors:


If the application has a rich collection of use cases (e.g., a text-editing program),
then a model-view-controller architecture is an appropriate choice; if there are
only a few use cases, showing that the program’s of computations are limited to
just one or two forms (e.g., a Celsius-to-Fahrenheit convertor), then a simpler
architecture, say, one that uses only a controller and a view, might be used.
Extremely simple use cases warrant a one-component architecture.

3. For each of the architecture’s components, specify classes:


The case study in Chapter 5 showed us how to write a specification for a class.
We use this specification technique to design the classes for an application’s
model, whose methods are responsible for doing computations. Similarly, we
design one or more classes for the view, which displays the results. (See again
the case study in Chapter 5, which specified an output-view class.) Finally, we
specify the controller component’s main method and describe how the controller
activates the methods in model and the view.

4. Write and test the individual classes:


From each specification of each class, we write the Java coding of the class’s
attributes and methods. We test the classes one at a time; we can do this
by writing small main methods that invoke the methods within the class. (The
section, “Testing Methods and Classes,” presented later in this chapter, presents
the details.) Or, if when using an IDE, we can type method invocations to the
IDE, which sends them to the class and lets us see the results.

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.

Beginning programmers often jump directly from collecting use-case behaviors to


writing and testing an entire application. But this short-cut makes it more difficult
to detect the source of program errors and much more time consuming to repair them.
We now consider a case study that employs the methodology.
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 259

6.8 Case Study: Bank Accounts Manager


A bank-accounts manager is a program that helps a user track her bank account bal-
ance — the user enters the deposits and withdrawals, and the program maintains and
displays a running balance. Say that we must design and build such an application,
following the methodology just described. To better understand what the program
must do, we begin by collecting use-case behaviors.

6.8.1 Collecting 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:

• a request to deposit, accompanied by a dollars, cents numerical amount

• a request to withdraw, accompanited by a dollars, cents amount

• a request to quit the program

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

of $50.50 would display

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

6.8.2 Selecting a Software Architecture


The use-case behaviors make clear that the application must maintain a bank account,
and there must be methods for depositing and withdrawing from the account. This
strongly suggests that we include in the application’s architecture a model class that
holds the bank account and its methods.
The use cases also indicate that an output-view component must display the
window that shows the account’s balance. Finally, a controller is needed to generate
input dialogs, send messages to the model to update the account, and send messages
to the output view to display the results. This gives us the standard model-view-
controller architecture, like the one displayed in Figure 9.

6.8.3 Specifying the Model


The model of the bank account must be specified by a class that remembers the the
bank account’s balance and owns methods for depositing and withdrawing money.
The account’s balance will be remembered by a field variable — an attribute.
Table 10 presents the specification for class BankAccount, the model. We see the
attribute and the two methods. Note also that
• Because the class’s constructor method requires an argument, we list the con-
structor as part of the interface specification.

• The attribute is listed as a private variable.

• 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.

6.8.4 Writing and Testing the Model


The algorithms for each of the model’s methods are straightforward. For example,
the one for deposit(int amount): boolean goes
262

Figure 6.10: specification of class BankAccount

BankAccount models a single bank


account
Constructor method:
BankAccount(int initializes the bank account with initial balance
initial balance)
Attribute
private int balance the account’s balance, in cents; should always be a
nonnegative value
Methods
deposit(int amount): add amount, a nonnegative integer, to the account’s
boolean balance. If successful, return true, if unsuccessful,
leave balance alone and return false
withdraw(int amount): attempt to withdraw amount, a nonnegative inte-
boolean ger, from the balance. If successful, return true, if
unsuccessful, leave balance alone and return false

1. If amount is a nonnegative, then add amount to the account’s balance.

2. Otherwise, issue an error message and ignore the deposit.

3. Return the outcome.

The class based on the specification appears in Figure 11.


Because attribute balance is labelled private, deposits and withdrawals are han-
dled by methods deposit and withdraw—this prevents another malicious object from
“stealing money” from the account! The comment attached to balance states the
representation invariant that the field’s value should always be nonnegative; the rep-
resentation invariant is a “pledge” that all methods (especially the constructor method
and withdraw) must preserve.
In addition to the methods listed in Table 10, the class contains an extra method,
getBalance, which returns the account’s balance. When an attribute, like balance,
is listed in a specfication, it is a hint that a public method, like getBalance, will be
included in the class to report the attribute’s current value. A method that does
nothing more than report the value of an attribute is called an accessor method.
The model class has the behaviors needed to maintain a bank account, but it does
not know the order in which deposits and writhdrawals will be made—determining
this is the responsibility of the controller part of the application.
Now that the model is written, we should test it. As hinted in the previous
paragraph, we test the model by constructing an object from it and sending a sequence
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 263

Figure 6.11: model class for bank account


import javax.swing.*;
/** BankAccount manages a single bank account */
public class BankAccount
{ private int balance; // the account’s balance
// representation invariant: balance >= 0 always!

/** Constructor BankAccount initializes the account


* @param initial amount - the starting account balance, a nonnegative. */
public BankAccount(int initial amount)
{ if ( initial amount >= 0 )
{ balance = initial amount; }
else { balance = 0; }
}

/** deposit adds money to the account.


* @param amount - the amount of money to be added, a nonnegative int
* @return true, if the deposit was successful; false, otherwise */
public boolean deposit(int amount)
{ boolean result = false;
if ( amount >= 0 )
{ balance = balance + amount;
result = true;
}
else { JOptionPane.showMessageDialog(null,
"BankAcccount error: bad deposit amount---ignored");
}
return result;
}

...
264

Figure 6.11: model class for bank account (concl.)


/* withdraw removes money from the account, if it is possible.
* @param amount - the amount of money to be withdrawn, a nonnegative int
* @return true, if the withdrawal was successful; false, otherwise */
public boolean withdraw(int amount)
{ boolean result = false;
if ( amount < 0 )
{ JOptionPane.showMessageDialog(null,
"BankAcccount error: bad withdrawal amount---ignored"); }
else if ( amount > balance )
{ JOptionPane.showMessageDialog(null,
"BankAcccount error: withdrawal amount exceeds balance");
}
else { balance = balance - amount;
result = true;
}
return result;
}

/* getBalance reports the current account balance


* @return the balance */
public int getBalance()
{ return balance; }
}

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:

public static void main(String[] args)


{ BankAccount tester = new BankAccount(0); // construct a test object
// now, try the getBalance method:
System.out.println("Initial balance = " + tester.getBalance());

// 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());

// continue in this fashion....


}

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.

6.8.5 Specifying the View Components


Table 12 lists the interface specifications for the input and output components. We
use an output view to build the window that displays the account’s balance, and
for the first time, we write an input-view class whose responsibility is to disassemble
input commands, like

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.

6.8.6 A Second Look at the Software Architecture


Once the specifications of the various components are written, it is standard to insert
the class, method, and attribute names from the specifications into the architectural
diagram. Figure 13 shows this for the bank-account manager. This picture is called
266

Figure 6.12: specification for view components

BankReader reads bank transac-


tions from the user
Methods:
readCommand(String message): reads a new command line, prompting the user with
char message. It returns the first character of the user’s
response.
readAmount(): int returns the integer value included in the most re-
cently read input line

BankWriter writes transactions


for a bank account
Constructor method:
BankWriter(String title, initializes the writer, where title is inserted in the
BankAccount b) window’s title bar, and b is the bank account whose
transactions is displayed
Methods:
showTransaction(String displays the result of a monetary bank transation,
message, int amount) where message lists the transaction and amount is
the numerical amount
showTransaction(String displays the result of a monetary bank transation,
message) where message lists the transaction.

Figure 6.13: bank-account manager

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.

6.8.7 Writing the View Classes


As stated by its specification, the input-view will read a line of input and disassemble
it into a command letter and a numerical amount. To do this disassembly, we can
use some of the methods on strings that were first presented in Table 5, Chapter 3.
We review the methods that will prove useful to us now:
• For a string, S, S.trim() examines S and returns a result string that looks like
S but with leading and trailing blanks removed. (Note: S is unchanged by this
method—a new string is constructed and returned.) A standard use of trim
goes like this:

String input = " some data ";


input = input.trim();

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,

char letter = input.charAt(0);

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’ )
{ ... }

• Finally, a segment (substring) can be extracted as a new string by means of the


substring method. For example, S.substring(start, end) examines string
S and constructs a new string that consists of exactly the characters from S
starting at index start and extending to index end - 1.
For example,

String first_two = input.substring(0, 3);

assigns to first two a string consisting of the first two characters from string
input. Another example is

String remainder = input.substring(1, input.length());


6.8. CASE STUDY: BANK ACCOUNTS MANAGER 269

Figure 6.14: input-view class for bank account manager


import javax.swing.*;
/** BankReader reads bank transactions from the user */
public class BankReader
{ private String input line; // holds the most recent input command line

/** Constructor BankReader initializes the input reader */


public BankReader()
{ input line = ""; }

/** readCommand reads a new command line


* @param message - the prompt to the user
* @return the first character of the command */
public char readCommand(String message)
{ // read the input line, trim blanks and convert to upper case:
input line = JOptionPane.showInputDialog(message).trim().toUpperCase();
return input line.charAt(0); // return the leading character
}

/** 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.)

Table 5 from Chapter 3 lists additional methods for string computation.


Figure 14 displays the input-view class matching the specification.
The BankReader uses readCommand to receive a new input command and return its
character code (e.g., "D" for “deposit”); a second method, readAmount, extracts the
numerical amount embedded in the command. Of course, the initial character code
is not part of the numerical amount, so the statement,

String amount = input_line.substring(1, input_line.length());

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,

answer = (int)(dollars_cents * 100);

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

Figure 6.15: output-view class for bank-account manager


import java.awt.*;
import javax.swing.*;
import java.text.*;
/** BankWriter writes bank transactions */
public class BankWriter extends JPanel
{ private int WIDTH = 300; // width and depth of displayed window
private int DEPTH = 200;
private BankAccount bank; // the address of the bank account displayed
private String last transaction = ""; // the transaction that is displayed

/** Constructor BankAccount initializes the writer


* @param title - the title bar’s text
* @param b - the (address of) the bank account displayed by the Writer */
public BankWriter(String title, BankAccount b)
{ bank = b;
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle(title);
my frame.setSize(WIDTH, DEPTH);
my frame.setVisible(true);
}

public void paintComponent(Graphics g)


{ g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, DEPTH); // paint the background
g.setColor(Color.black);
int text margin = 50;
int text baseline = 50;
g.drawString(last transaction, text margin, text baseline);
g.drawString("Current balance = $" + unconvert(bank.getBalance()),
text margin, text baseline + 20);
}

/** unconvert reformats a cents amount into a dollars,cents string */


private String unconvert(int i)
{ double dollars cents = i / 100.00;
return new DecimalFormat("0.00").format(dollars cents);
}

...
272

Figure 6.15: output-view class for bank-account manager (concl.)


/** showTransaction displays the result of a monetary bank transation
* @param message - the transaction
* @param amount - the amount of the transaction */
public void showTransaction(String message, int amount)
{ last transaction = message + " " + unconvert(amount);
this.repaint();
}

/** showTransaction displays the result of a bank transation


* @param message - the transaction */
public void showTransaction(String message)
{ last transaction = message;
this.repaint();
}
}

6.8.8 Controller Construction and Testing


The last piece of the puzzle is the controller, AccountController, which fetches the
user’s transactions, dispatches them to the model for computation, and directs the
results to the output view. The controller’s algorithm for processing one transaction
goes as follows:

1. Read the command.

2. If the command is ’Q’, then quit processing.

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);

• Within the processTransactions method, this style of nested conditional is


used to process the D and W commands:
if ( TEST1 )
{ S1 }
else if ( TEST2 )
{ S2 }
else if ( TEST3 )
{ S3 }
else ...

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 ...
}
}

As a rule, omitting an else-arm’s brackets is dangerous, and you should do so


only when writing a sequence of nested ifs in the above format.
• When a deposit or withdrawal command is processed, the writer object is sent a
showTransaction message that has two parameters; when an illegal command is
received, writer is sent a showTransaction message that has just one parameter.
Because showTransaction is an overloaded method name in writer, the two
forms of messages go to distinct methods. (Review Figure 15.)
• After processTransactions processes a command, it sends a message to itself to
restart itself and process yet another command. When a method restarts itself,
it is called a recursive invocation. Recursive invocation is the simplest way to
make a method repeat an activity, but in the next chapter we study more direct
techniques for repetition.
274

• processTransactions’s recursive invocations continue until a Q command is re-


ceived, at which time the method does nothing, in effect halting the invocations
of the method.

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:

public class BankAccount


{ // there are no attributes

public BankAccount(int initial_amount) { } // does nothing

public boolean deposit(int amount)


{ System.out.println("deposit of " + amount);
return true; // the method must return some form of result
}

public boolean withdraw(int amount)


{ System.out.println("withdrawal of " + amount);
return true;
}

public int getBalance()


{ return 0; }
}

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

Figure 6.16: controller for bank account manager


/** AccountController maintains the balance on a bank account. */
public class AccountController
{ // fields that remember the view and model objects:
private BankReader reader; // input-view
private BankWriter writer; // output-view
private BankAccount account; // model

/** Constructor AccountController builds the controller


* @param r - the input-view object
* @param w - the output-view object
* @param a - the model object */
public AccountController(BankReader r, BankWriter w, BankAccount a)
{ reader = r;
account = a;
writer = w;
}

/** processTransactions processes user commands until a Q is entered */


public void processTransactions()
{ char command = reader.readCommand("Command (D,W,Q) and amount:");
if ( command == ’Q’ ) // quit?
{ } // terminate method by doing nothing more
else { if ( command == ’D’ ) // deposit?
{ int amount = reader.readAmount();
boolean ok = account.deposit(amount);
if ( ok )
{ writer.showTransaction("Deposit of $", amount); }
else { writer.showTransaction("Deposit invalid ", amount); }
}

else if ( command == ’W’ ) // withdraw?


{ int amount = reader.readAmount();
boolean ok = account.withdraw(amount);
if ( ok )
{ writer.showTransaction("Withdrawal of $", amount); }
else { writer.showTransaction("Withdrawal invalid ", amount); }
}
else { writer.showTransaction("Illegal command: " + command); }
this.processTransactions(); // send message to self to repeat
}
}
}
276

Figure 6.16: controller for bank account manager (concl.)


/** AccountManager starts the application that maintains a bank account. */
* Inputs: a series of commands of the forms,
* D dd.cc (deposit),
* W dd.cc (withdraw), or
* Q (quit), where dd.cc is a dollars-cents amount
* Outputs: a display of the results of the transactions */
public class AccountManager
{ public static void main(String[] args)
{ // create the application’s objects:
BankReader reader = new BankReader();
BankAccount account = new BankAccount(0);
BankWriter writer = new BankWriter("BankWriter", account);
AccountController controller =
new AccountController(reader, writer, account);
// start the controller:
controller.processTransactions();
}
}

the interaction of the components. Often, a mixture of dummy classes and a


completed classes, added one at a time, can ease this difficulty.

6.8.9 Testing the Assembled Application


Once all the classes are specified, written, and thoroughly tested individually, it is
time to assemble them and perform integration testing. As noted in the previous
section, integration testing can be done incrementally, by starting with the controller
component and dummy classes and then replacing the dummy classes by finished
classes one at a time. A more bold approach is to combine all the finished classes and
go straight to work with testing.
As stated earlier, integration testing should attempt, at least, all the use-case
behaviors that motivated the design of the application.

Exercises
1. Here are some small exercises regarding testing:

(a) Write a tester-controller that creates a BankAccount object, deposits 1000,


withdraws 700, and asks for the balance. Print the balance in the command
window.
6.8. CASE STUDY: BANK ACCOUNTS MANAGER 277

(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:

/** depositInterest increases the account with an interest payment,


* calculated as (interest_rate * current_balance)
* @param rate - the interest rate, a value between 0.0 and 1.0
* @return the amount deposited into the account; if the deposit cannot
* be performed, return 0 */
public int depositInterest(double rate)

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.

6.8.10 Multiple Objects from the Same Class


We finish the bank-account case study with a small but crucial modification: Since
many people have multiple bank accounts, we modify the account-manager applica-
tion to manage two distinct bank accounts simultaneously—a “primary account” and
a “secondary account.”
The architecture in Figure 12 stays the same, but we annotate its arcs with num-
bers to make clear that the one controller now collaborates with two models. As a
consequence, each model is mated to its own output view, meaning there are two
output views in the complete program. See Figure 17.
In the Figure, read the numbering of the form
1 m
A B

as stating, “each 1 A-object is coupled to (uses or sends messages to) m B-objects.”


(An unannotated arrow is a 1-1 coupling.)
278

Figure 6.17: program architecture with two bank accounts

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:

• P, a request to do transactions on the primary bank account

• S, a request to do transactions on the secondary bank account

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

Figure 6.18: controller for managing two bank accounts


/** AccountController2 maintains the balance on two bank accounts, a primary
* account and a secondary account.
* inputs: P (use primary account), S (use secondary account),
* D dd.cc (deposit), W dd.cc (withdraw), Q (quit)
* outputs: the transactions displayed on two windows */
public class AccountController2
{ private BankReader reader; // input view

// the primary bank-account: model and its output-view:


private BankAccount primary account;
private BankWriter primary writer;
// the secondary bank-account: model and its output-view:
private BankAccount secondary account;
private BankWriter secondary writer;

// 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

/** AccountController2 initializes the bank accounts */


public AccountController2(BankReader r, BankAccount a1, BankWriter w1,
BankAccount a2, BankWriter w2)
{ reader = r;
primary account = a1;
primary writer = w1;
secondary account = a2;
secondary writer = w2;
// start processing with the primary account:
account = primary account;
writer = primary writer;
}

/** 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

Figure 6.18: controller for managing two bank accounts (concl.)


public void processTransactions()
{ char command = reader.readCommand("Command (P,S, D,W,Q):");
if ( command == ’Q’ ) // quit?
{ }
else { if ( command == ’D’ ) // deposit?
{ int amount = reader.readAmount();
account.deposit(amount);
writer.showTransaction("Deposit of $", amount);
}
else if ( command == ’W’ ) // withdraw?
{ int amount = reader.readAmount();
boolean ok = account.withdraw(amount);
if ( ok )
{ writer.showTransaction("Withdrawal of $", amount); }
else { writer.showTransaction("Withdrawal invalid", amount); }
}
else if ( command == ’P’ ) // work with primary account?
{ resetAccount(primary account, primary writer); }
else if ( command == ’S’ ) // work with secondary account?
{ resetAccount(secondary account, secondary writer); }
else { writer.showTransaction("Illegal command"); }
this.processTransactions(); // send message to self to repeat
}
}
}

/** AccountManager2 maintains two bank accounts */


public class AccountManager2
{ public static void main(String[] args)
{ BankReader reader = new BankReader();
// create the models and their views:
BankAccount primary account = new BankAccount(0);
BankWriter primary writer
= new BankWriter("Primary Account", primary account);
BankAccount secondary account = new BankAccount(0);
BankWriter secondary writer
= new BankWriter("Secondary Account", secondary account);
// construct the controller and start it:
AccountController2 controller = new AccountController2(reader,
primary account, primary writer, secondary account, secondary writer);
controller. processTransactions();
}
}
282

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

6.9 More about Testing Classes and Methods


Complex systems, like cars and televisions, are assembled from separate components
that are designed, manufactured, and tested separately. It is critical for us to design,
write, and test software components (classes) in a similar way. How do we test a
class?

6.9.1 Testing Individual Methods


As suggested by the earlier case study, to test a class, we must test its methods. When
testing an individual method, think of the method as a “little main method,” whose
input arguments arrive by means of actual parameters—we write lots of invocations
of the methods with lots of different actual parameters. Therefore, the techniques
we learned earlier for testing entire applications apply to testing individual methods:
(Reread the section, “Testing a Program that Uses Input,” in Chapter 4, for details.)
Test a method with both “white box” and “black box” techniques that ensure
that each statement in the method is executed in at least one test case.
In particular, when a method contains if-statements, be certain to formulate
enough tests so that every arm of every if-statement is executed by at least one test.
Although this strategy does not guarantee correctness of the entire method, it should
be obvious that an error inside the arm of a conditional will never be detected if the
arm is never executed! You might draw a flowchart of a method to help devise the
test cases that ensure each possible control flow is tested at least once.

6.9.2 Testing Methods and Attributes Together


Unfortunately, we cannot always test each method of a class independently—if a
class’s method references a field variable (an attribute), then the value in the attribute
affects how the method behaves. To complicate matters further, if the attribute is
shared by multiple methods in the class, then the manner in which all the methods
use the field becomes crucial to the correct behavior of each method.
We deal with this situation in two stages: The first is to consider how a single
method interacts with a class’s attrbutes; the second is to consider the order in which
a family of methods use the attributes.
We consider the first stage now: Given one single method, we must identify the
attributes used by the method. For each attribute, we list the range of values that
the method would find acceptable as the attribute’s value. Then, we initialize the
attribute with sample values within this range and do multiple invocations of the
method to observe the interactions between the method and the attribute. At the
same time, whenever the method changes the value of an attribute, we check that the
new value assigned to the attribute is acceptable.
284

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

// We are not yet testing the constructor method, so leave it empty:


public BankAccount() { }

public boolean withdraw(int amount)


{ boolean result = false;
if ( amount < 0 )
{ ... }
else ...
return result;
}
}
Now we are ready for a round of test invocations of withdraw with a variety of argu-
ments. After each invocation, we check the value of balance and verify that withdraw
assigned an acceptable value.
As we systematically test a class’s methods one by one, we will reach a consensus
regarding the attribute’s range of values that is acceptable to all the class’s methods.
This helps us establish the representation invariant for each attribute and eases the
second stage of method testing.

6.9.3 Testing a Suite of Methods


Because methods typically share usage of a class’s attributes, the order in which a
class’s methods are invoked can affect the behavior of the methods themselves. For
example, it is potentially a problem if a new BankAccount object is constructed with an
initial balance of zero, and we invoke the withdraw method before doing any deposits!
A more subtle problem arises when several methods share an attribute, and one
method inserts an unacceptable value into the attribute; this negatively impacts the
other methods, which assume that the attribute always holds acceptable values.
We might tackle this situation by generating test cases that include all possible
sequences of method invocations, but this is daunting. A better solution is to rely on
the representation invariants that we formulated based on the testing of the individual
methods:
For each attribute, we list a representation invariant. For each method we validate
that, if the method starts execution when all attributes have values that are
6.9. MORE ABOUT TESTING CLASSES AND METHODS 285

acceptable to their representation invariants, then when the method completes,


all attributes hold values that are acceptable to their representation invariants.

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.

6.9.4 Execution Traces


When one method is invoked, it might invoke other methods. In this situation, you
might require extra assistance in understanding exactly what one method does in
terms of other methods; a good way of obtaining this understanding is by generating
a mechanical execution trace during a test invocation of a method.
In this text, we have seen handwritten execution traces from time to time. Indeed,
the best way to learn about execution patterns of an invoked method is to write
an execution trace by hand! But if you lack pencil and paper, it is possible to
make method generate its own execution trace by this simple trick: insert println
statements into its body and the bodies of the methods it invokes—have each method
print, at entry to its body, the values of its parameters and the field variables it uses.
When the method’s body finishes, it prints the values of fields and important local
variables.
Of course, if you are using an IDE you can use the IDE’s “debugger” to generate
execution traces, and you do not have to insert the println statements. Instead,
you tell the debugger to insert “breakpoint” lines at the statements where you wish
to see the values of parameters and fields. Then, you start the program, and the
debugger lets the program execute until a breakpoint line is encountered. At the
breakpoint line, the debugger pauses the program, and you can ask the debugger to
286

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:

public void deposit(int amount)


{ System.out.println("deposit ENTRY: amount = " + amount
+ " balance = " + balance);
balance = balance + amount;
System.out.println("deposit EXIT: balance = " + balance);
}

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:

public class BankAccount


{ private int balance; // representation invariant: balance >= 0

public BankAccount(int initial_balance)


{ balance = 0; } // dummy --- this assignment makes the invariant true

public void deposit(int amount)


{ balance = balance + amount; }

public boolean withdraw(int amount)


{ return true; } // dummy

public int getBalance() { return 0; } // dummy


}

The bodies of the yet-to-be-coded methods, BankAccount, withdraw, and getBalance,


are represented by harmless, dummy methods that preserve balance’s representation
invariant.

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);
}

• relational operator (from Figure 6):

if ( (dollars < 0) || (cents < 0) || (cents > 99) )


{ ... }

• thrown exception (from Figure 8):

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

• overloaded method name (from Figure 15):

/** showTransaction displays the result of a monetary bank transation


* @param message - the transaction
* @param amount - the amount of the transaction */
public void showTransaction(String message, int amount)
{ last_transaction = message + " " + unconvert(amount);
repaint();
}

/** showTransaction displays the result of a bank transation


* @param message - the transaction */
public void showTransaction(String message)
{ last_transaction = message;
repaint();
}

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

• recursive invocation: an invocation where a method sends a message to restart


itself.
• model: a component in an application that “models” the programming problem
and computes its answer (e.g., class BankAccount is models a customer’s bank
account and is used as the central component of the bank-account-manager
application in Figure 12).
• attribute: a private field variable inside a model class. The field holds informa-
tion about what the model’s “internal state.”
• model-view-controller architecture: a design of application divided into these
components: a model for modelling the problem and computing the answer; a
view for reading input and displaying output; and a controller for determining
control flow.
• coupling: a “collaboration” where one class depends on another class to do
its work; drawn as an arrow connecting the two classes (e.g., in Figure 12,
BankWriter is coupled to BankAccount).

• 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).

• The benefits for building an application in the model-view-controller architec-


ture are

– The classes can be reused in other applications.


– The application is organized into standardized, recognized, manageably
sized parts that have specific duties.
– The parts are isolated so that alterations to one part do not force rewriting
of the other parts.

6.11 Programming Projects


1. Locate the current exchange rates for 3 different currencies (e.g., dollar, euro,
and yen). Write a currency converter tool that lets its user input an amount in
one currency and receive as output the equivalent amount in another currency
that the user also requests.

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

(For example, 10 degrees Celsius is 283.15 degrees Kelvin.)

Write a class TempCalculator with these methods:


6.11. PROGRAMMING PROJECTS 291

celsiusIntoFahrenheit(double translate the Celsius temperature, c, into its


c): double Fahrenheit value and return it as a double
fahrenheitIntoCelsius(double translate the Fahrenheit temperature, f, into its
f): double Celsius value and return it as a double
celsiusIntoKelvin(double c): translate the Celsius temperature, c, into its
double Kelvin value and return it as a double
kelvinIntoCelsius(double k): translate the Kelvin temperature, k, into its
double Celsius value and return it as a double
fahrenheitIntoKelvin(double translate the Fahrenheit temperature, c, into its
f): double Kelvin value and return it as a double
kelvinIntoFahrenheit(double translate the Kelvin temperature, k, into its
k): double Fahrenheit value and return it as a double

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.

3. Rebuild the change-making program of Figure 3, Chapter 3, so that it uses a


model that fits this specification:

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.)

The following formulas will be helpful to you in writing the class:

• The formula for calculating the correct annual payment:

payment = (1+i) ∗p∗i


y

(1+i)y −1
monthlyP ayment = payment/12.0

for principal, p, interest rate, i, and years, y.


• The reduced principal due to a monthly payment:

newP rincipal = ((1 + (i/12))remaining principal) − payment

where i and payment are as above.

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.

5. The distance travelled by an automobile, moving at initial velocity, V0 , at ac-


celeration, a, for time, t, is: distance = V0 t + (1/2)a(t2 ) Use this formula to
6.11. PROGRAMMING PROJECTS 293

“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

to refund a customer’s coins when asked, and to refuse service if no coffee or


tea is left.
Two of the methods might be

insertMoney(int amount) the customer inserts the amount of money into


the vending machine, and the machine remem-
bers this.
askForCoffee(): boolean the customer requests a cup of coffee. If the cus-
tomer has already inserted an adequate amount
of money, and the machine still has coffee in it,
the machine produces a cup of coffee, which it
signifies by returning the answer, true. If the
machine is unable to produce a cup of coffee,
false is returned as the answer.

Based on your specification, write a model class.


Next, write an output-view of the vending machine and write an input-view
and controller that help a user type commands to “insert” money and “buy”
coffee and tea from the machine.

10. Write a model class that has methods for computing an employee’s weekly pay.
The methods should include

• pay calculation based on hours worked and hourly payrate,


• overtime pay calculation, based on overtime as 1.5 times the regular payrate
for hours worked over 40,
• deduction of payroll tax. Use this table:
– 20% of pay, for 0-30 hours of work
– 25% of pay, for 31-40 hours of work
– 28% of pay, for 41 or more hours of work
• and deduction of retirement benefits (use 5% of total pay).

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.

6.12 Beyond the Basics


6.12.1 The Logic within the Conditional Statement

6.12.2 Interface Specifications and Integration

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

• writing class interface specifications that contain preconditions and postcondi-


tions to help with testing and integrating the classes

6.12.1 The Logic of the Conditional Statement


Relational operations are of course logical connectives, as anyone who has studied
symbolic logic will readily testify. Indeed, we can use the laws of symbolic logic
to establish standard logical equivalences. For example, for arbitrary boolean-typed
expressions, P and Q, it is always the case that the computation of
!(P || Q)

yields the same results as that of


296

!P && !Q

Indeed, here are valuable logical equivalences that simplify relational expressions:

!(P || Q) is equivalent to !P && !Q


!(P && Q) is equivalent to !P || !Q
!!P is equivalent to P
P && (Q && R) is equivalent to P && Q && R is equivalent to (P && Q) && R
P || (Q || R) is equivalent to P || Q || R is equivalent to (P || Q) || R

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:

!(x < 0 || (y <= 10 && y != 1))


is equivalent to !(x < 0) && !(y <= 10 && y != 1)
is equivalent to x >= 0 && (!(y <= 10) || !( y != 1))
is equivalent to x >= 0 && (y > 10 || y == 1)

Since we used relational operations to compress the structure of nested condition-


als, we should not be surprised to learn that there is a connection between symbolic
logic and the conditional statement. The connection can be stated 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:

public int abs(int n)


{ int answer;
if ( n < 0 )
{ answer = -n; }
else { answer = n; }
return answer;
}
6.12. BEYOND THE BASICS 297

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|

else // assume !(n < 0), that is, 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

Our analysis of the else-arm’s test produces these additional assumptions:


if ( hour == 0 )
// assume hour == 0 && hour >= 0 && hour < 12
// that is, hour == 0
{ answer = answer + "12"; }
// goal: append correct hour to answer

else // assume !(hour == 0) && hour >= 0 && hour < 12


// that is, hour > 0 && hour < 12
{ 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.

(a) !(x < 0 || x > 59)


(b) !(!(x == 0) && y != 1)
(c) !(x > 0) && !(y != 1 || x >= 0)
(d) !(x == 0 && (x != x) && true)

2. Given this class:

public class Counter


{ private int count;

public Counter()
{ count = 0; }

public boolean increment()


{ count = count + 1;
return true;
}

public boolean equalsZero()


{ return (count == 0); }
}

Say that we have this situation:

Counter c = new Counter();


if ( TEST )
{ ... }

(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

(b) Similarly demonstrate that P || Q behaves differently than Q || P; also


for P == Q and P != Q.
(c) Table 5 shows that for the expression, P && Q, if P computes to false, then
Q is not computed at all. Explain why this is different from a semantics of
&& where both P and Q must compute to answers before the answer of the
conjunction is computed.

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;
}

6.12.2 Interface Specifications and Integration


Testing classes one by one does not ensure that the completed application will behave
as expected—sometimes the interaction between objects goes unexpectedly wrong.
This is not supposed to happen, but the problem can arise when the designer of a
method has not specified clearly (i) the conditions under which an object’s method
should be used and (ii) the form of result the method produces. A standard format,
6.12. BEYOND THE BASICS 301

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:

/* withdraw removes money from the account, if it is possible.


* @param amount - the amount of money to be withdrawn, a nonnegative int
* @return true, if the withdrawal was successful; return false, if the
* amount to be withdrawn was larger than the account’s balance */
public boolean withdraw(int amount)

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:

/** deposit adds money to the account.


* @param amount - the amount of money to be added, a nonnegative int */
public void deposit(int amount)

Because an interface specification is a specification for object connection, it should


state all key information for using the object’s methods; a programmer should not
be required to read the body of a method that someone else has written in order
to invoke it—reading the method’s specification should suffice. This requirement is
essential for creating classes that others can use.
Programmers often call a class’s interface specification its API (application pro-
gramming interface).
If a large application is developed by a group of people, and if the people agree to
use specifications like the ones in this Chapter, it becomes possible for each person to
design, code, and test a part of the application independently of the other parts. (At
the testing stage, one can write “dummy classes” to represent the unfinished classes
that others are writing.)
The specifications used in this text are somewhat informal, but they are better
than none at all. Indeed, this is why the designers of the Java language developed
the javadoc documentation program—a programmer can generate useful APIs while
writing her programs.
Indeed, since javadoc does not execute the class that it reads, it will generate API
documentation for an incomplete class. For example, if a multi-class application must
be written by a team of people, it is useful to have the API documentation for all
classes available as soon as possible, even before the classes are completed. One can
write the interface specifications, attach empty method bodies to them, and supply
them to javadoc. Here is such a dummy class from which javadoc generates a useful
API:

/** BankAccount manages a single bank account */


public class BankAccount
{
/** Constructor BankAccount initializes the account
* @param initial_amount - the starting account balance, a nonnegative. */
public BankAccount(int initial_amount)
{ }

/** deposit adds money to the account.


6.12. BEYOND THE BASICS 303

* @param amount - the amount of money to be added, a nonnegative int */


public void deposit(int amount)
{ }

/* withdraw removes money from the account, if it is possible.


* @param amount - the amount of money to be withdrawn, a nonnegative int
* @return true, if the withdrawal was successful; false, if the amount
* to be withdrawn was larger than the account’s balance */
public boolean withdraw(int amount)
{ }

/* getBalance reports the current account balance


* @return the balance */
public int getBalance()
{ }
}

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; }

/** comment for g goes here.


* @param x - parameter info */
public void g(String x) { System.out.println(x); }
}

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

Patterns of Repetition: Iteration and


Recursion
7.1 Repetition

7.2 While Loops

7.3 Definite Iteration

7.3.1 Definite-Iteration Example: Painting a Bulls-Eye

7.4 Nontermination

7.5 Indefinite Iteration: Input Processing

7.5.1 Indefinite Iteration: Searching

7.6 For-Statements

7.7 Nested Loops

7.8 Writing and Testing Loops

7.9 Case Study: Bouncing Ball Animation

7.10 Recursion

7.10.1 An Execution Trace of Recursion

7.11 Counting with Recursion

7.11.1 Loops and Recursions

7.11.2 Counting with Multiple Recursions

7.12 Drawing Recursive Pictures

7.13 Summary

7.14 Programming Projects

7.15 Beyond the Basics

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

7.2 While Loops


At the beginning of the previous section, we saw two algorithms for tightening a car’s
wheel and finding a television show. We can rewrite the two algorithms in “while-loop
style,” where we begin the algorithm with the word, “while,” followed by a true-false
test, followed by an action to perform as long as the test is true:
• while ( lug nut still loose ) { rotate nut clockwise one turn; }

• while ( favorite TV show not found ) { press the channel button; }


The phrase within the parentheses is the “test expression”; when the test expression is
true, the statement within the set braces is performed once, then the entire statement
repeats (“loops”). Eventually (we hope!) the test expression becomes false, and the
while-loop ceases.
We can write while-loops in Java; the format is
while ( TEST ) { BODY }

where TEST is a boolean-typed expression and BODY is a sequence of (zero or more)


statements.
With Java’s while-loop, we can rewrite the method that prints the reciprocals
from 1/2 to 1/20. The algorithm goes like this:
1. Set variable denominator = 2. The variable remembers which reciprocal to print
next.

2. Do this: while ( denominator <= 20 ) { print 1.0/denominator and add one to


denominator. }

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:

1. The TEST expression is computed.


7.2. WHILE LOOPS 307

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

and indeed, this is the origin of the term, “loop.”


Loops fall into two categories: definite iteration, where the number of the loop’s
iterations is known the moment the loop is started, and indefinite iteration, where it
is not. Also, it is possible for a loop’s iterations to be unbounded (that is, the loop
never stops). We study all three behaviors in the examples in the sections that follow.

Exercises
1. What will these while-loops print?

(a) String s = "";


while ( s.length() != 5 )
{ s = s + ’a’;
System.out.println(s);
}
(Recall that the length operation returns the length of a string; for exam-
ple,
String s = "abcde";
int i = s.length();

assigns 5 to i.)
(b) int countdown = 10;
while ( countdown != 0 )
{ System.out.println(i);
countdown = countdown - 1;
}
308

(c) int countdown = 5;


int countup = -1;
while ( countdown > countup )
{ countdown = countdown - 1;
System.out.println(countdown + " " + countup);
countup = countup + 1;
}
(d) while ( false )
{ System.out.println("false"); }
(e) String s = "";
while ( s.length() < 6 )
{ s = s + "a";
if ( (s.length() % 2) == 1 )
{ System.out.println(s); }
}

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.)

7.3 Definite Iteration


A loop performs definite iteration when the number of the loop’s iterations is known
the moment the loop is started. The example in the previous section displayed defi-
nition iteration, since it was clear at the outset that the loop would repeat exactly 19
times. Definite-iteration loops arise often, and it is worth studying their development.
Say that we must construct a method that computes the average score of a stu-
dent’s exams. The method first asks the student to type the number of exams and
then the method reads the exam scores, one by one, totals them, and computes the
average score.
There is a numerical pattern to computing an average score; if the student has
taken N exams, then the average score is calculated by this formula:
average = (exam1 + exam2 + ... + examN) / N

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.

3. While count not yet equals how many, do the following:

• Ask the user for the next exam score and add it to total points.
• Increment count by 1.

4. Calculate the average score as total points / how many.

The above algorithm can be refined into this Java-like coding:

int how_many = HOW MANY SCORES TO READ;


double total_points = 0.0;
int count = 0;
while ( count != how_many )
{ int score = READ INTEGER FROM THE USER;
total_points = total_points + score;
count = count + 1;
}
return (total_points / how_many);

The algorithm is presented as a Java method, computeAverage, in Figure 1. At each


iteration of the loop, the method generates a dialog to read the next exam score, and a
println statement helps us see the loop’s progress. Say that the user wishes to average
the scores, 8, 5, 6, and 7. The invocation, System.out.println(computeAverage(4)),
produces this trace information:

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,

total_points == exam_1 + exam_2 + ... + exam_count


310

Figure 7.1: while-loop to compute average score


import javax.swing.*;

/** 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 */
public double computeAverage(int how many)
{ 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 )
// at each iteration: total points == exam 1 + exam 2 + ... + exam count
{ // ask the user for the next exam score:
String input = JOptionPane.showInputDialog("Type next exam score:");
int score = new Integer(input).intValue();
// add it to the total:
total points = total points + score;
count = count + 1;
// print a summary of the progress:
System.out.println("count = " + count + "; total = " + total points);
}
// at conclusion: total points == exam 1 + exam 2 + ... + exam how many
return (total points / how many);
}

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.

It is absolutely crucial to understand the details of loop execution, so we examine


part of the execution trace of the example. Say that the method has been invoked as
computeAverage(4); once variables count and total points are initialized, we have
7.3. DEFINITE ITERATION 311

the following configuration:

int how many == 4 double total points == 0.0 int count == 0


>while ( count != how many )
{ int score = ...;
count = count + 1;
total points = total points + score;
}

The loop’s test evaluates to true, so execution moves into the body:

int how many == 4 double total points == 0.0 int count == 0


while ( true )
{ >int score = ...;
count = count + 1;
total points = total points + score;
}

The user types 8 as the first exam score; the loop’s body revises the variables’ values:

int how many == 4 double total points == 8.0 int count == 1


while ( true )
{ ...
>}

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:

int how many == 4 double total points == 8.0 int count == 1


>while ( count != how many )
{ int score = ...;
count = count + 1;
total points = total points + score;
}

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:

int how many == 4 double total points == 13.0 int count == 2


while ( true )
{ ...
>}
312

The loop repeats twice more, reading the last two scores, and we reach the configu-
ration where count’s cell holds 4:

int how many == 4 double total points == 26.0 int count == 4


>while ( count != how many )
{ int score = ...;
count = count + 1;
total points = total points + score;
}

The test evaluates to false, and the loop is abandoned:

int how many == 4 double total points ==26.0 int count == 4


while ( false )
{ ... }
¿

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 loop counter that remembers how many iterations are completed,

• a test expression that examines the loop counter to see if all the iterations are
completed,

• a statement that increments the loop counter to record the iteration.

For a loop counter, count, the pattern of definite iteration often looks like this:

int count = INITIAL VALUE;


while ( TEST ON count )
{ EXECUTE LOOP BODY;
INCREMENT count;
}

We have seen this pattern used twice already.


Occasionally, count is incremented at the beginning of the loop body:

int count = INITIAL VALUE;


while ( TEST ON count )
{ INCREMENT count;
EXECUTE LOOP BODY;
}
7.3. DEFINITE ITERATION 313

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 */

2. Write an execution trace for this example:

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

4. Write a main method that does the following:

(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

5. Many standard mathematical definitions are computed with definite-iteration


loops. For each of the definitions that follow, write a method that computes
the definition:

(a) The summation of a nonnegative integer, i, is defined as follows:


summation(i) = 0 + 1 + 2 + ... + i

For example, summation(4) is 0 + 1 + 2 + 3 + 4 = 10. So, write a method


that computes summation whose header line reads like this:
public int summation(int i)

(b) The iterated product of two nonnegative integers, a and b, goes


product(a, b) = a * (a+1) * (a+2) * ... * b

(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

For example, 5! is 1 * 1 * 2 * 3 * 4 * 5 = 120. (Important: Because


the values of m! grow large quickly as m grows, use the Java data type
long (“long integer”) instead of int for the argument and answer of this
function.)
(d) If you enjoy using sines and cosines, then use the factorial method in the
previous exercise to implement these classic methods:
7.3. DEFINITE ITERATION 315

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)

7.3.1 Definite-Iteration Example: Painting a Bulls-Eye


The definite-iteration pattern for loops can help us to draw interesting graphical
patterns. As an example, perhaps we want to paint a “bulls-eye” pattern with n
alternating red and white rings, where n’s value is given by the program’s user. When
n is 7, we see:

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

new BullsEyeWriter(7, 140)

which draws a 7-ring bulls-eye of diameter 140 pixels.

Exercises
1. Construct this object:

new BullsEyeWriter(21, 200)

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.)

2. Write this Java method:


7.3. DEFINITE ITERATION 317

Figure 7.2: method that paints a bulls-eye


/** paintBullsEye paints a bulls-eye
* @param x position - of the upper left corner of the bulls-eye
* @param y position - of the upper left corner of the bulls-eye
* @param rings - the number of rings in the bulls-eye
* @size - the bulls-eye’s diameter
* @param g - the graphics pen */
public void paintBullsEye(int x position, int y position,
int rings, int size, Graphics g)
{ int count = 0; // no rings painted just yet
int diameter = size; // diameter of next ring to paint
int ring width = size / rings; // set width for each ring
Color color = Color.red;
while ( count != rings )
// invariant: have painted count rings so far
{ // calculate upper left corner of ring to paint, centering the
// the ring within the bulls-eye by dividing by 2:
int new x position = x position + ((ring width * count)/2);
int new y position = y position + ((ring width * count)/2);
// paint the ring:
g.setColor(color);
g.fillOval(new x position, new y position, diameter, diameter);
// increment variables:
count = count + 1;
diameter = diameter - ring width;
if ( color == Color.red )
{ color = Color.white; }
else { color = Color.red; }
}
}
318

Figure 7.3: a panel that displays a bulls-eye


import javax.swing.*;
import java.awt.*;
/** BullsEyeWriter paints a bulls-eye on a panel */
public class BullsEyeWriter extends JPanel
{ private int rings; // how many rings appear in the bulls-eye
private int size; // the size of the completed bulls-eye
private int panel width; // width of the graphics panel
private int offset = 20; // where to start painting the bulls-eye

/** Constructor BullsEyeWriter constructs the panel and frames it.


* @param number of rings - how many rings in the bulls-eye
* @param total size - the diameter of the bulls-eye */
public BullsEyeWriter(int number of rings, int total size)
{ rings = number of rings;
size = total size;
panel width = size + (2 * offset);
// construct frame for this panel:
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("Bulls-Eye");
my frame.setSize(panel width, panel width);
my frame.setVisible(true);
}

/** paintComponent draws the bulls-eye


* @param g - the graphics pen that does the drawing */
public void paintComponent(Graphics g)
{ g.setColor(Color.yellow); // paint background yellow:
g.fillRect(0, 0, panel width, panel width);
paintBullsEye(offset, offset, rings, size, g);
}

... paintBullsEye appears here ...


}
7.4. NONTERMINATION 319

/** paintSquares paints n squares across the left-to-right diagonal


* of a graphics window
* @param x_position - of the upper left corner of the first square
* @param y_position - of the upper left corner of the first square
* @param n - the number of squares to paint
* @size - the width of each square
* @param g - the graphics pen */
private void paintsquares(int x_position, int y_position,
int n, int size, Graphics g)

3. Figure 9 of Chapter 5 contains a helper method, paintAnEgg, that paints an egg


on a graphics window. Use this method to write a method, paintBullsEyeOfEggs,
that paints a “bulls-eye” of n centered eggs in a graphics window.

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:

/** printReciprocals displays the decimal values of the fractions,


* 1/2, 1/3, 1/4, ..., one at a time: When invoked,
* it shows a message dialog with the information, "1/2 = 0.5",
* and when the user presses OK, it shows another message dialog
* that says, "1/3 = 0.3333333333", and when the user presses OK,
* it shows another message dialog, "1/4 = 0.25", and so on.... */
public void printReciprocals()

Now, write an application that invokes it. Test the application for as long as
you have the patience to do so.

2. Reconsider the paintBullsEye method in Figure 2. What happens if the method


is asked to paint zero rings? A negative number of rings? Revise the method
so that it will always terminate.
Study the invariant property for the loop in the computeAverage method in
Figure 1. When the loop in that method concludes, we know that total points
== exam 1 + exam 2 + ... + exam how many holds true.
Now, review modifiedComputeAverage. Is the loop invariant the same as that
in Figure 1? Can we conclude the same result when the loop terminates as we
did in Figure 1? What can we conclude about the value of total points when
the loop in modifiedComputeAverage terminates?

7.5 Indefinite Iteration: Input Processing


Many programs are designed to interact with a user indefinitely; for example, the
bank-account manager application in Chapter 6 processed as many account transac-
tions as its user chose to enter. The appplication did this by sending a message to
restart itself after processing each input request, but we see in this Section that a
while-loop is a simpler technique for repetitive input processing.
We can build on the exam-average method in Figure 1 to illustrate the technique.
Say that we modify the method so that it does not know how many exam scores it
must read. Instead, the user enters exam scores, one by one, into input dialogs until
the user terminates the process by pressing the Cancel button. The method handles
this behavior with a loop that terminates when Cancel is pressed.
A first attempt at the revised method’s algorithm might go,
1. while ( the user has not yet pressed Cancel ), do the following:

• Generate a dialog to read the next exam score;


322

• 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.

2. Compute the average of the exams.

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:

boolean processing = true;


while ( processing )
{ generate a dialog to read the next exam score;
if ( the user pressed Cancel )
{ processing = false; } // time to terminate the loop!
else { add the score to the total and increment the count; }
}

Figure 4 displays the modified method from Figure 1.


There is a standard way to process a user’s input with a while-loop: A sequence
of input transactions are submitted, one at a time, and each input transaction is
processed by one iteration of the loop. The loop terminates when there are no more
input transactions. Here is the pattern:

boolean processing = true;


while ( processing )
{ READ AN INPUT TRANSACTION;
if ( THE TRANSACTION INDICATES THAT THE LOOP SHOULD STOP )
{ processing = false; }
else { PROCESS THE TRANSACTION; }
}

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:

public static void main(String[] args)


{ boolean processing = true;
7.5. INDEFINITE ITERATION: INPUT PROCESSING 323

Figure 7.4: processing input transactions


import javax.swing.*;

/** computeAverage computes the average of test scores submitted by a user.


* The user terminates the submissions by pressing Cancel.
* @return the average of the test scores
* @throw RuntimeException if the user presses Cancel immediately */
public double computeAverage()
{ double total points = 0.0; // the total of all test scores
int count = 0; // the quantity of tests read so far
boolean processing = true;
while ( processing )
// at each iteration: total points == exam 1 + exam 2 + ... + exam count
{ String input = JOptionPane.showInputDialog
("Type next exam score (or press Cancel to quit):");
if ( input == null ) // was Cancel pressed?
{ processing = false; } // time to terminate the loop
else { int score = new Integer(input).intValue();
total points = total points + score;
count = count + 1;
}
}
if ( count == 0 ) // did the user Cancel immediately?
{ throw new RuntimeException("computeAverage error: no input supplied"); }

return (total points / count);


}

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);
}

2. Write an application that invokes the method in Figure 4 to compute an average


of some exam scores. The application displays the average in a message dialog.
3. Write an application that reads as many lines of text as a user chooses to type.
324

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";

Recall that the \n is the newline character.)


4. Revise the bank-account manager of Chapter 6 so that it uses a while-loop to
process its input transactions. (Hint: Revise processTransactions in class
AccountController in Figure 16, Chapter 6.)

7.5.1 Indefinite Iteration: Searching


In the real world, searching for a missing object is an indefinite activity, because we
do not know when we will find the object. Programs can also go “searching”—for
example, a program might search a computerized telephone directory for a person’s
telephone number. A small but good example of computerized searching is finding
a specific character in a string. Searches use an important pattern of loop, so we
develop the character-search example in detail.
Recall these two useful operations on strings from Table 5, Chapter 3:
• The length operation returns the length of a string; for example,

String s = "abcde";
int i = s.length();

assigns 5 to i. The length of an empty string, "", is 0.


• We use the charAt operation to extract a character from a string:

String s = "abcde";
char c = s.charAt(3);

assigns ’d’ to c. (Recall that a string’s characters are indexed by 0, 1, 2, and


so on.)
Now we consider how to search a string, s, for a character, c: We examine the
string’s characters, one by one from left to right, until we find an occurrence of c or
we reach the end of s (meaning c is not found). We use an integer variable, index,
to remember which character of s we should examine next:
7.5. INDEFINITE ITERATION: INPUT PROCESSING 325

1. Set index to 0.

2. While ( c is not yet found, and there are still unexamined characters within s
), do the following:

(a) If s.charAt(index) is c, then we have found the character at position index


and can terminate the search.
(b) Otherwise, increment index.
The while-loop suggested by the algorithm looks like this:
int index = 0;
boolean found = false; // remembers whether c has been found in s
while ( !found && index < s.length() )
{ if ( s.charAt(index) == c )
{ found = true; }
else { index = index + 1; }
}

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

Figure 7.5: searching for a character in a string


/** findChar locates the leftmost occurrence of a character in a string.
* @param c - the character to be found
* @param s - the string to be searched
* @return the index of the leftmost occurrence of c in s;
* return -1 if c does not occur in s */
public int findChar(char c, String s)
{ boolean found = false; // remembers if c has been found in s
int index = 0; // where to look within s for c
while ( !found && index < s.length() )
// invariant:
// (1) found == false means c is not any of chars 0..(index-1) in s
// (2) found == true means c is s.charAt(index)
{ if ( s.charAt(index) == c )
{ found = true; }
else { index = index + 1; }
}
if ( !found ) // did the loop fail to find c in all of s?
{ index = -1; }
return index;
}

The loop can terminate in two possible ways:

• !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.

This pattern was used to write the method in Figure 5.


Here is another use of the searching pattern—determining whether an integer is
a prime number. Recall that an integer 2 or larger is a prime if it is divisible (with
no remainder) by only itself and 1. For example, 3, 13, and 31 are primes, but 4 and
21 are not.
To determine whether an integer n is prime, we try dividing n by each of the
numbers in the set, {2, 3, 4, ..., n/2 }, to try to find a number that divides into n
with remainder zero. (There is no need to try integers larger than n/2, of course.) If
our search for a divisor fails, then n is a prime.
The method’s algorithm is based on the searching pattern:

boolean item_found = false;


current = n / 2; // start searching here for possible integer divisors
7.5. INDEFINITE ITERATION: INPUT PROCESSING 327

Figure 7.6: method for prime detection


/** isPrime examines an integer > 1 to see if it is a prime.
* @param n - the integer > 1
* @return 1, if the integer is a prime;
* return the largest divisor of the integer, if it is not a prime;
* @throw RuntimeException, if the argument is invalid, that is, < 2. */
public int isPrime(int n)
{ int answer;
if ( n < 2 )
{ throw new RuntimeException("isPrime error: invalid argument " + n ); }
else { boolean item found = false;
int current = n / 2; // start search for possible integer divisor
while ( !item found && current > 1 )
// invariant:
// (1) item found == false means n is not divisible
// by any of n/2, (n/2)-1, ...down to... current+1
// (2) item found == true means current divides n
{ if ( n % current == 0 )
{ item found = true; } // found a divisor
else { current = current - 1; } // try again
}
if ( item found )
{ answer = current; } // current is the largest divisor of n
else { answer = 1; } // n is a prime
}
return answer;
}

// and count downwards towards 2


while ( !item_found && current > 1 )
{ if ( current divides into n with remainder 0 )
{ item_found = true; } // current is a divisor of n
else { current = current - 1; } // select another possible divisor
}

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

2. Use the searching pattern to write the following method:

/** powerOfTwo determines whether its argument is a power of 2.


* @param n - the argument, a nonnegative int
* @return m, such that n == 2^m, if n is a power of 2
* return -1, if n is not a power of 2 */
public int powerOfTwo(int n)

What is the “collection” that you are “searching” in this problem?

7.6 For-Statements
Definite-iteration loops pervade computer programming. When we use this pattern
of definite iteration,

{ int i = INITIAL VALUE;


while ( TEST ON i )
{ EXECUTE LOOP BODY;
INCREMENT i;
}
}

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:

for ( int i = INITIAL VALUE; TEST ON i; INCREMENT i; )


{ EXECUTE LOOP BODY; }

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:

for ( int denominator = 2; denominator <= 20; denominator = denominator+1 )


// invariant: 1/2, ...up to..., 1/(denominator-1) printed so far
{ System.out.println("1/" + denominator + " = " + (1.0 / denominator)); }
7.7. NESTED LOOPS 329

Compare this to the original while-loop:

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 is more concise than the while-statement, because it displays


the initialization, test, and increment of the loop counter all on one line.

• The for-statement suggests to the reader that the loop is a definite iteration.

Begin Footnote: Java’s for-statement can be used to compute an indefinite iter-


ation (by omitting the INCREMENT i part), but we do not do this in this text. End
Footnote
The for-statement is particularly useful for systematically computing upon all the
items in a set; here is a loop that easily prints all the characters in a string s, one per
line, in reverse order:

for ( int i = s.length() - 1; i >= 0; i = i - 1 )


// invariant: printed characters at s.length()-1 ...downto... (i+1)
{ System.out.println(s.charAt(i)); }

Exercises
1. Rewrite the computeAverage method in Figure 1 so that it uses a for-statement.

2. Use a for-statement to write a function, reverse, that receives a string pa-


rameter, s, and returns as its result the string that looks like s reversed, e.g.,
reverse("abcd") returns "dcba".

3. Rewrite the method in Figure 2 to use a for-statement.

4. Rewrite into for-loops the while-loops you wrote in the answers to the Exercises
for the “Definite Iteration” Section

7.7 Nested Loops


A loop may be placed within the body of another loop; a nested loop is the result.
Nested loops are the natural solution to problems that require a “table” of answers,
so we begin with two examples that build tables.
330

Computing a Multiplication Table


We might wish to generate a 4-by-5 table of all the mutiplications of 0..3 by 0..4. The
output might appear like this:

To do this, we must generate all possible combinations of i * j, when i varies in the


range 0..3 and i varies in the range 0..4. The algorithm for printing the multiplications
might go:

1. for i varying from 0 to 3, do the following: print i * 0, i * 1, ..., i * 4.

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:

1. for i varying from 0 to 3, do the following:

• for j varying from 0 to 4, do the following: print i * j

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:

for ( int i = 0; i <= 3; i = i + 1 )


{ // invariant: printed 0*x up to (i-1)*x, for all values x in 0..4
for ( int j = 0; j <= 4; j = j + 1 )
// invariant: printed i*0 up to i*(j-1)
{ System.out.print(i + "*" + j + "=" + (i * j) + " "); }
System.out.println();
}

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:

1. for i varying from 0 to n-1, do the following:

• 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

Figure 7.7: method to paint a chessboard


/** paintBoard paints an n-by-n chessboard of red and white squares
* @param start x - position of the upper left corner of the board
* @param start y - position of the upper left corner of the board
* @param total rows - the number of rows of the board
* @param square size - the width of each square, in pixels
* @param g - the graphics pen */
private void paintBoard(int start x, int start y,
int total rows, int square size, Graphics g)
{ for ( int x = 0; x < total rows; x = x + 1 )
// invariant: have painted x rows so far
{ // calculate position of row x:
int x position = start x + (x * square size);
for ( int y = 0; y < total rows; y = y + 1 )
// invariant: have painted y squares of row x
{ // calculate position of the y-th square:
int y position = start y + (y * square size);
if ( ((x + y) % 2) == 0 ) // is square x,y a red one?
{ g.setColor(Color.red); }
else { g.setColor(Color.white); }
g.fillRect(x position, y position, square size, square size);
}
}
}

We write the method so that a chessboard can be painted at whatever position,


number of rows, and size a user desires. Figure 7 shows the method, which can be
invoked by a panel’s paintComponent method, as we saw in the bulls-eye-painting
example earlier in this chapter in Figures 2 and 3.

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:

1. Set answer = ""

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:

1. Set index = 0 (this variable is used to look at individual characters within


alpha), and set searching for c position = true.

2. While ( searching for c position and there are still characters in alpha to
examine ), do the following

• If c is less-or-equals to alpha.charAt(index), then we have found the cor-


rect position for inserting c, so set searching for c position = false;
• Else, increment index (and keep searching).

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

Figure 7.8: methods for alphabetizing a string


/** alphabetize computes a string that has the same characters as
* its argument but arranged in alphabetical order
* @param s - the input string
* @return the alphabetized string */
public String alphabetize(String s)
{ String answer = "";
for ( int i = 0; i != s.length(); i = i + 1 )
// update the answer by inserting s.charAt(i) into it:
{ answer = insertAlphabetically(s.charAt(i), answer); }
return answer;
}

/** insertAlphabetically inserts c into alpha, preserving


* alphabetical ordering. */
private String insertAlphabetically(char c, String alpha)
{ int index = 0; // the position where we will possibly insert c
boolean searching for c position = true;
while ( searching for c position && index < alpha.length() )
{ if ( c <= alpha.charAt(index) ) // should c be placed at index?
{ searching for c position = false; }
else { index = index + 1; }
}
// ‘‘break’’ alpha into two pieces and place c in the middle:
return alpha.substring(0, index)
+ c + alpha.substring(index, alpha.length());
}

Alphabetization is a special case of sorting, where a collection of items are arranged


ordered according to some standardized ordering. (For example, a dictionary is a
collection of words, sorted alphabetically, and a library is a collection of books, sorted
by the catalog numbers stamped on the books’ spines.)

Exercises
1. Write nested loops that generate the addition table for 0+0 up to 5+5.

2. Write this method:

/** removeDuplicateLetters constructs a string that contains the


* same letters as its argument except that all duplicate letters
* are removed, e.g., for argument, "butterflies", the result is
* "buterflis"
* @param s - the argument string
7.8. WRITING AND TESTING LOOPS 335

* @return a string that looks like s but with no duplicates */


public String removeDuplicateLetters(String s)

3. Write nested loops that print this pattern:

0 0
1 0 1 1
2 0 2 1 2 2
3 0 3 1 3 2 3 3

4. Write nested loops that print this pattern:

0 3 0 2 0 1 0 0
1 3 1 2 0 1
2 3 2 2
3 3

7.8 Writing and Testing Loops


Loops are easily miswritten. For example, one must not inadvertently type an extra
semicolon—this example,

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;
}

This loop again attempts to compute the summation, 1 + 2 + ... + n, but it


forgets to add the starting value of i into its total.
Examples like these show that it is not always obvious when a loop iterates exactly
the correct number of times. When you test a loop with example data, be certain to
know the correct answer so that you can compare it to the loop’s output.
Here is another technical problem with loop tests: Never code a loop’s test ex-
pression as an equality of two doubles. For example, this loop

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

int n = ... ; // the input value


int total = 0;
int i = 0;
while ( i != n )
{ total = total + i;
i = i + 1;
}

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,

// at each iteration: total_points == exam_1 + exam_2 + ... + exam_count

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

7.9 Case Study: Bouncing Ball Animation


Because a loop lets us repeat an action over and over, we can write a loop that paints
a graphical image over and over, changing the image slightly with each iteration—
This gives the illusion of movement. An application that displays moving objects in
a graphics window is an animation.
A simple but classic animation is a ball bouncing within a box:

(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

Figure 7.9: architecture of a simple animation

Controller
run() View
{ while (true) paintModel()
{ wait one time unit; { model.getState();
model.updateState(); repaint();
view.paintModel(); }
}
}

Model
getState()
updateState()

4. Write and test the individual classes.

5. Integrate the classes into the architecture and test the system.

Let’s move through these steps:

Behaviors and Architecture

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.

Specifying the Model Subassembly

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

class BallAndBox maintains a ball that moves within a box


Attributes
the box’s size, the ball’s size,
the ball’s position, the ball’s ve-
locity
Methods
moveTheBall(int time units) Updates the model’s state by moving the ball a
distance based on its current position, its veloc-
ity, and the amount of time, time units, that
have passed since the last time the state has
been updated.
getTheState() Returns the current position of the ball, its size,
and the dimensions of the box.
This initial specification is a bit problematic: The attributes of the ball and the
box are mixed together, and this will cause trouble if we later modify the animation,
say by using two balls or using a different form of container. The specification of the
getTheState also has some confusion about what exactly is the state of the model.
These concerns motivate us to design the model as two classes—one class for the
ball and one for the box.
Let’s consider the ball’s class first—what are its attributes and what are its meth-
ods? Of course, a ball has a physical size and location (x- and y-coordinates). Because
it is moving in two-dimensional space, it has a velocity in both x- and y-coordinates.
The primary method the ball requires is the ability to move on request. This pro-
duces the specification of class MovingBall that appears in Table 10. In addition
to its mutator method, move, the final version of class MovingBall will also possess
accessor methods that return the ball’s radius, position, and so on.
The description of the move method in Figure 10 makes clear that the moving ball
must send messages to the box in which it is contained, to learn if it has hit a well and
must change its direction. This motivates us to design class Box. A box’s attributes
are just the positions of its walls, and assuming that the box is shaped as a square, it
suffices to remember just the box’s width. A box does not actively participate in the
moving-ball animation, but it must have methods that reply when asked if a given
position is in contact with a wall.
Table 11 shows one way to specify class Box.
At this point, the design of the model is mostly complete. Refinements might be
made as the programming problem is better understood, but the specifications are a
good starting point for developing the remainder of the program.

Implementing and Testing the Model


Now, we can write the classes. As usual, class Ball has accessor methods that yield
the values of its attributes. The most interesting method is move, which updates the
7.9. CASE STUDY: BOUNCING BALL ANIMATION 341

Figure 7.10: specification of moving ball

class MovingBall models a ball that moves in two-dimensional space


Attributes
int x pos, int y pos the center coordinates of the ball’s location
radius the ball’s radius (size)
int x velocity, int the ball’s horizontal and vertical velocities
y velocity
Method
move(int time units) moves to its new position based on its velocity the
the elapsed amount of time units, reversing direc-
tion if it comes into contact with a wall of the con-
tainer (the Box) that holds it.

Figure 7.11: specification of box

class Box models a square box


Attribute
int BOX SIZE the width of the square box
Methods
inHorizontalContact(int responds whether x position (a horizontal coordi-
x position): boolean nate) is in contact with one of the Box’s (leftmost
or rightmost) walls
inVerticalContact(int responds whether y position (a vertical coordi-
y position): boolean nate) is in contact with one of the Box’s (upper
or lower) walls

ball’s state; its algorithm goes

1. Move the ball to its new position.

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.)

Figure 12 shows the coding of class Ball’s move method.


The coding of class Box is simple, because the box’s state is fixed when the box
is constructed, so the box’s methods merely return properties related to the positions
of the box’s walls. See Figure 13.
342

Figure 7.12: model of moving ball


/** MovingBall models a moving ball */
public class MovingBall
{ private int x pos; // ball’s center x-position
private int y pos; // ball’s center y-position
private int radius; // ball’s radius

private int x velocity = +5; // horizonal speed; positive is to the right


private int y velocity = +2; // vertical speed; positive is downwards

private Box container; // the container in which the ball travels

/** Constructor MovingBall constructs the ball.


* @param x initial - the center of the ball’s starting horizontal position
* @param y initial - the center of the ball’s starting vertical position
* @param r - the ball’s radius
* @param box - the container in which the ball travels */
public MovingBall(int x initial, int y initial, int r, Box box)
{ x pos = x initial;
y pos = y initial;
radius = r;
container = box;
}

/** xPosition returns the ball’s current horizontal position */


public int xPosition()
{ return x pos; }

/** yPosition returns the ball’s current vertical position */


public int yPosition()
{ return y pos; }

/** radiusOf returns the ball’s radius */


public int radiusOf()
{ return radius; }

/** move moves the ball


* @param time units - the amount of time since the ball last moved */
public void move(int time units)
{ x pos = x pos + (x velocity * time units);
if ( container.inHorizontalContact(x pos) )
{ x velocity = -x velocity; } // reverse horizontal direction
y pos = y pos + (y velocity * time units);
if ( container.inVerticalContact(y pos) )
{ y velocity = -y velocity; } // reverse vertical direction
}
}
7.9. CASE STUDY: BOUNCING BALL ANIMATION 343

Figure 7.13: model of a box


/** Box models a box in which moving objects live */
public class Box
{ private int box size; // the size of the box

/** Constructor Box builds the box


* @param size - the size of the box */
public Box(int size)
{ box size = size; }

/** inHorizontalContact replies whether a position has contacted a


* horizontal wall.
* @param x position - the position examined
* @return true, if x position equals or exceeds the positions of the
* horizontal walls; return false, otherwise */
public boolean inHorizontalContact(int x position)
{ return (x position <= 0 ) || (x position >= box size); }

/** inVerticalContact replies whether a position has contacted a


* vertical wall.
* @param y position - the position examined
* @return true, if y position equals or exceeds the positions of the
* vertical walls; return false, otherwise */
public boolean inVerticalContact(int y position)
{ return (y position <= 0 ) || (y position >= box size); }

/** sizeOf returns the size of the box */


public int sizeOf()
{ return box size; }
}

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

Specifying and Implementing the View Subassembly


The ball and box become more interesting once we write a view to paint them. Since
the model is designed in two components, we propose a view class that paints the box
and a view class that paints the ball. This will make it easier to extend the animation
later to contain multiple moving balls, and it suggests the pleasant idea that each
model component should have its own view component. Here is a diagram of how the
view classes might be specified:

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.

Implementing the Controller


Finally, the program’s controller uses a loop to control the progress of the simulation;
its algorithm is

1. loop forever, doing the following steps:

• Tell the ball to move one time unit.


• Tell the writer to repaint the state of the animation on the display

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

Figure 7.14: view class for moving-ball simulation


import java.awt.*;
import javax.swing.*;
/** AnimationWriter displays a box with a ball in it. */
public class AnimationWriter extends JPanel
{ private BoxWriter box writer; // the output-view of the box
private BallWriter ball writer; // the output-view of the ball in the box

/** Constructor AnimationWriter constructs the view of box and ball


* @param b - the box’s writer
* @param l - the ball’s writer
* @param size - the frame’s size */
public AnimationWriter(BoxWriter b, BallWriter l, int size)
{ box writer = b;
ball writer = l;
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("Bounce");
my frame.setSize(size, size);
my frame.setVisible(true);
}

/** paintComponent paints the box and ball


* @param g - the graphics pen */
public void paintComponent(Graphics g)
{ box writer.paint(g);
ball writer.paint(g);
}
}
346

Figure 7.15: view classes for box and ball


import java.awt.*;
/** BoxWriter displays a box */
public class BoxWriter
{ private Box box; // the (address of the) box object that is displayed

/** Constructor BoxWriter displays the box


* @param b - the box that is displayed */
public BoxWriter(Box b)
{ box = b; }

/** paint paints the box


* @param g - the graphics pen used to paint the box */
public void paint(Graphics g)
{ int size = box.sizeOf();
g.setColor(Color.white);
g.fillRect(0, 0, size, size);
g.setColor(Color.black);
g.drawRect(0, 0, size, size);
}
}

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

/** Constructor BallWriter


* @param x - the ball to be displayed
* @param c - its color */
public BallWriter(MovingBall x, Color c)
{ ball = x;
balls color = c;
}

/** paint paints the ball on the view


* @param g - the graphics pen used to paint the ball */
public void paint(Graphics g)
{ g.setColor(balls color);
int radius = ball.radiusOf();
g.fillOval(ball.xPosition() - radius,
ball.yPosition() - radius, radius * 2, radius * 2);
}
}
7.10. RECURSION 347

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

Figure 7.16: controller and start-up class for moving-ball animation


/** BounceController controls a moving ball within a box. */
public class BounceController
{ private MovingBall ball; // model object
private AnimationWriter writer; // output-view object

/** Constructor BounceController initializes the controller


* @param b - the model object
* @param w - the output-view object */
public BounceController(MovingBall b, AnimationWriter w)
{ ball = b;
writer = w;
}

/** runAnimation runs the animation by means of an internal clock */


public void runAnimation()
{ int time unit = 1; // time unit for each step of the animation
int painting delay = 20; // how long to delay between repaintings
while ( true )
{ delay(painting delay);
ball.move(time unit);
System.out.println(ball.xPosition() + ", " + ball.yPosition());
writer.repaint(); // redisplay box and ball
}
}

/** delay pauses execution for how long milliseconds */


private void delay(int how long)
{ try { Thread.sleep(how long); }
catch (InterruptedException e) { }
}
}
7.10. RECURSION 349

Figure 7.16: start-up class for moving-ball animation (concl.)


import java.awt.*;
/** BounceTheBall constructs and starts the objects in the animation. */
public class BounceTheBall
{ public static void main(String[] args)
{ // construct the model objects:
int box size = 200;
int balls radius = 6;
Box box = new Box(box size);
// place the ball not quite in the box’s center; about 3/5 position:
MovingBall ball = new MovingBall((int)(box size / 3.0),
(int)(box size / 5.0),
balls radius, box);
BallWriter ball writer = new BallWriter(ball, Color.red);
BoxWriter box writer = new BoxWriter(box);
AnimationWriter writer
= new AnimationWriter(box writer, ball writer, box size);
// construct the controller and start it:
new BounceController(ball, writer).runAnimation();
}
}

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

To make a zero-layer cake, place an empty cake pan on the table.

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:

bakeACake(0) = place an empty pan on the table


bakeACake(N + 1) = bakeACake(N) and top it with one more layer

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

Figure 7.17: class Cake


/** class Cake is a simple model of a multi-layer cake */
public class Cake
{ int how many layers;

/** Constructor Cake constructs a 0-layer cake---a pan */


public Cake()
{ how many layers = 0; }

/** addALayer makes the Cake one layer taller */


public void addALayer()
{ how many layers = how many layers + 1; }

/** displayTheCake prints a representation of the cake */


public void displayTheCake()
{ ... left as a project for you to do ... }
}

and top it with one more layer


=> ((bakeACake(0)
and top it with one more layer))
and top it with one more layer)
and top it with one more layer
=> ((place an empty pan on the table
and top it with one more layer))
and top it with one more layer)
and top it with one more layer

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);

constructs a 0-layer cake. More interesting is


Cake c = bakeACake(3);

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

Figure 7.18: a recursive method for cake making


/** bakeACake makes a multi-layer cake
* @param layers - the number of the cake’s layers
* @return the cake */
public Cake bakeACake(int layers)
{ Cake result;
if ( layers == 0 )
// then the first equation applies:
// bakeACake(0) = place an empty pan on the table
{ result = new Cake(); }
else // the second equation applies:
// bakeACake(N + 1) = bakeACake(N) and top it with one more layer
{ result = bakeACake(layers - 1);
result.addALayer();
}
return result;
}

and eventually assigned to variable c. To understand the process, we should study


an execution trace.

7.10.1 An Execution Trace of Recursion


We now study how the method in Figure 12 contructs its answer by recursion in an
example invocation of bakeACake(3).
The invocation causes 3 to bind to the the formal parameter, layers; it and the
local variable, result, are created:

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;
}

The invocation causes a fresh, distinct activation record of bakeACake to be executed:

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.

Further execution causes two more copies of bakeACake to be invoked, producing


354

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

how many layers == 0


...

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

how many layers == 0


...

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

how many layers == 1


...

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:

Cake c = new Cake();


for ( int i = 1; i != N; i = i + 1 )
{ c.addALayer(); }

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.

7.11 Counting with Recursion


Many computing problems that require counting can be solved by recursion. One
classic example is permutation counting: Say that we want to calculate the total
number of permutations (orderings) of n distinct items. For example, the permutations
of the three letters a, b, and c are abc, bac, bca, acb, cab, and cba—there. are six
permutations.
Permutation generation has tremendous importance to planning and scheduling
problems, for example:

• 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

(n+1)! = (n+1) * n!, when n is nonnegative


(Notice that the starting number is lowered to zero, and that 1! equals 1, just like
we determined earlier.)
It is easy to translate these two equations into a recursively defined method that
computes factorials of integers:
public int fac(int n)
{ int answer;
if ( n == 0 )
{ answer = 1; } // 0! = 1
else { answer = n * fac(n - 1); } // (n+1)! = (n+1) * n!
return answer;
}
If you test the function, fac, on the computer, you will see that, when the argument
to fac is greater than 13, the answer is so huge that it overflows the internal computer
representation of an integer!
BeginFootnote: Unfortunately, a Java int can hold a value only between the range
of about negative 2 billion and positive 2 billion. EndFootnote.
For this reason, we revise method fac so that it uses Java’s long version of in-
tegers. We also make check that the argument to the function is not so large that
the computed answer will overflow the internal representation of a long integer. See
Figure 19 for the implementation of the factorial function.
When you test the factorial function, say by
System.out.println("4! = " + factorial(4));
you will find that the invocation of factorial(4) generates the invocation to factorial(3),
and so on, down to factorial(0). Then, once the invoked functions starting return-
ing their answers, a series of four multiplications are performed, which compute the
result, 24. This pattern of invocations and multiplications are displayed when we
compute with factorial’s algorithm:
4! => 4 * 3!
=> 4 * (3 * 2!)
=> 4 * (3 * (2 * 1!))
=> 4 * (3 * (2 * (1 * 0!)))
=> 4 * (3 * (2 * (1 * 1)))
=> 4 * (3 * (2 * 1)) => 4 * (3 * 2) => 4 * 6 => 24
7.11. COUNTING WITH RECURSION 359

Figure 7.19: recursively defined method that computes factorial


/** factorial computes n! for n in the range 0..20.
* (note: answers for n > 20 are too large for Java to compute,
* and answers for n < 0 make no sense.)
* @param n - the input; should be in the range 0..20
* @return n!, if n in 0..20
* @throw RuntimeException, if n < 0 or n > 20 */
public long factorial(int n)
{ long answer;
if ( n < 0 || n > 20 )
{ throw new RuntimeException("factorial error: illegal input"); }
else { if ( n == 0 )
{ answer = 1; } // 0! = 1
else { answer = n * factorial(n - 1); } // (n+1)! = (n+1) * n!
}
return answer;
}

7.11.1 Loops and Recursions


Now that we understand the reason why the factorial function gives the answers it
does, we can study its Java coding and ask if there is an alternative way to program
the function. Indeed, because the coding of factorial invokes itself once, we can
rearrange its statements into a for-loop. Here is the loop version of the function:
public long factorial(int n)
{ long answer;
if ( n < 0 || n > 20 )
{ throw new RuntimeException("factorial error: illegal input"); }
else { answer = 1;
int count = 0;
while ( count != n )
// invariant: answer == count!
{ count = count + 1;
answer = count * answer;
}
}
return answer;
}
The loop mimicks the sequence of multiplications that compute the answer. Since
the statements in the body of the loop bear no resemblance to the original recursive
algorithm, we rely on the loop’s invariant to understand why the loop computes the
correct result.
360

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.

7.11.2 Counting with Multiple Recursions


Some problems simply cannot be understood and resolved without recursive tech-
niques, and here is a counting problem that makes this point.
Perhaps we are obsessed by insects and want to know how reproductive house flies
are. Say that, in its lifetime, one house fly lays exactly two eggs. Each egg produces
a fly that itself lays exactly two eggs. If this process occurs for n generations of
egg-laying, how many flies result? (Assume that the flies never die.)
Because each fly gives birth to two more flies, which themselves give birth to even
more, the pattern of counting cannot be done by a simple iteration—we must solve
the problem by thinking about it as a counting problem that “decomposes” into two
more counting problems.
An answer to the problem is that the total number of flies produced by the original
fly is the sum of the flies produced by the first egg, plus the flies produced by the
second egg, plus the original fly. If we say that the original fly is n-generations old,
then its child flies are one generation younger—each are n-1 generations old. We have
this recursive equation:
flies_at_generation(n) = flies_at_generation(n-1) + flies_at_generation(n-1) + 1

A newly hatched fly is 0-generations old:


flies_at_generation(0) = 1

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:

int one_egg_produces = fliesAtGeneration(n - 1);


answer = (2 * one_egg_produces) + 1;

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

Fib(n) = Fib(n-1) + Fib(n-2), when n >= 2

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!)

5. Here is a recursive algorithm for computing the product of a sequence of non-


negative integers:

product(a, b) = 1, when b < a


product(a, b) = product(a, b-1) * b, when b >= a

Write a recursively defined method that computes product.

6. Even an activity as simple as the addition of two nonnegative integers can be


solved recursively in terms of just “-1” and “+1”:

add(0, b) = b
add(a, b) = add(a-1, b) + 1, when a > 0

(a) Write a recursively defined method that computes this definition.


(b) In a similar style, define multiplication, mult, of two nonnegative integers
in terms of “-1” and add.
(c) Define exponentiation, exp, of two nonnegative integers in terms of “-1”
and mult.

7. Ackermann’s function is yet another famous recursive definition:

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

Write a recursively defined method based on this definition. The function is


famous because its recursions cause the values of the the second parameter
to zoom upwards while the first parameter slowly decreases, yet the function
always terminates.

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.)

7.12 Drawing Recursive Pictures


Recursively defined methods are ideal for drawing beautiful recursive pictures.
Perhaps we wish to paint a picture of an egg resting in front of a picture of
another egg resting in front of a picture of another egg, ..., until the eggs recede into
nothingness:

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.

Of course, Step 1 is a recursive reference, and an argument, size, will be used to


determine how large to paint the picture. A recursive invocation will decrease size to
draw the background picture, and the drawings of the background pictures terminate
when size reaches 0.
Figure 20 shows the method, named paintPicture, that uses this algorithm.
When paintPicture draws a picture, it first sends a message to itself to paint
a background picture at 0.8 size. The regress of drawing successively smaller back-
ground pictures halts when paintPicture notices that a background picture would
have a 0-sized egg. In this way, the recursions “count down” to 0, and the quantity of
recursive invocations count how many background pictures must be drawn to fill the
entire frame. When the background pictures are drawn, from smallest to largest, the
eggs are properly positioned, smallest to largest.
Figure 21 shows an ever more clever example, where a “field of eggs” is drawn by
a method that invokes itself two times:

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

Figure 7.20: class that paints a recursively defined picture


import java.awt.*; import javax.swing.*;
/** RecursivePictureWriter displays a recursively defined picture of an
* egg in front of a picture (of eggs). */
public class RecursivePictureWriter extends JPanel
{ private double scale = 0.8; // the size of the background picture relative
// to the foreground
private int width = 400; // frame width and depth
private int depth = 200;
private int first egg = 150; // size of the egg in the foreground

/** Constructor RecursivePictureWriter creates the window */


public RecursivePictureWriter()
{ JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("RecursivePictureWriter");
my frame.setSize(width, depth);
my frame.setBackground(Color.white);
my frame.setVisible(true);
}

/** paintComponent paints the recursively defined picture


* @param g - the ‘‘graphics pen’’ */
public void paintComponent(Graphics g)
{ g.setColor(Color.white);
g.fillRect(0, 0, width, depth); // paint the background
paintPicture(width, depth, first egg, g);
}

/** paintPicture paints a picture of an egg in front of a picture of eggs.


* @param right border - the right border of the picture painted
* @param bottom - the bottom border of the picture painted
* @param size - the size of the egg to be painted in the foreground
* @param g - the graphics pen */
private void paintPicture(int right border, int bottom,
int size, Graphics g)
{ // paint the background picture first:
int background size = (int)(size * scale);
if ( background size > 0 ) // is the background worth painting?
{ paintPicture((int)(right border * scale), (int)(bottom * scale),
background size, g);
}
// paint an egg in the foreground:
paintAnEgg(right border - size, bottom, size, g);
g.setColor(Color.black);
g.drawRect(0, 0, right border, bottom); // draw the border
}
...
366

Figure 7.20: class that paints a recursively defined picture (concl.)


/** paintAnEgg paints an egg in 2-by-3 proportion
* @param left edge - the position of the egg’s left edge
* @param bottom - the position of the egg’s bottom
* @param width - the egg’s width
* @param g - the graphics pen */
private void paintAnEgg(int left edge, int bottom, int width, Graphics g)
{ int height = (2 * width) / 3;
int top = bottom - height;
g.setColor(Color.pink);
g.fillOval(left edge, top, width, height);
g.setColor(Color.black);
g.drawOval(left edge, top, width, height);
}

public static void main(String[] args)


{ new RecursivePictureWriter(); }
}

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:

private void paintAnEgg(int left_edge, int bottom, int width, Graphics g)


{ int height = (2 * width) / 3;
int top = bottom - height;
g.setColor(Color.black);
g.drawOval(left_edge, top, width, height);
}

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

Figure 7.21: a field of eggs


import java.awt.*; import javax.swing.*;
/** EggFieldWriter displays a binary tree depicted as a ‘‘field of eggs’’ */
public class EggFieldWriter extends JPanel
{ private int size = 20; // size for eggs
private int total layers of eggs = 7; // how many layers to paint
private int frame width = 600;
private int frame height = 200;

/** Constructor EggFieldWriter creates the window and makes it visible */


public EggFieldWriter()
{ JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("EggFieldWriter");
my frame.setSize(frame width, frame height);
my frame.setBackground(Color.white);
my frame.setVisible(true);
}

/** paintComponent fills the window with the field of eggs


* @param g - the ‘‘graphics pen’’ */
public void paintComponent(Graphics g)
{ paintEggField(220, 200, total layers of eggs, g); }

/** paintEggField paints two fields of eggs behind a lead egg


* @param left border - the left edge of the lead egg in the field
* @param baseline - the bottom of the lead egg
* @param layer - how many layers of eggs to draw
* @param g - the graphics pen */
private void paintEggField(int left border, int baseline, int layer,
Graphics g)
{ if ( layer > 0 )
{ int egg size = size * layer; // invent a size for the lead egg
// paint right sub-egg field:
paintEggField(left border + ((2 * egg size) / 3),
baseline - (egg size / 3), layer - 1, g);
// paint left sub-egg field:
paintEggField(left border - (egg size / 2),
baseline - (egg size / 3), layer - 1, g);
// paint leading egg:
paintAnEgg(left border, baseline, egg size, g);
}
}
// see Figure 20 for method paintAnEgg
}
368

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); }

– indefinite iteration, where the number of repetitions is not known. The


pattern for indefinite iteration of input processing reads
boolean processing = true; // this variable announces when it’s time to stop
while ( processing )
{ READ AN INPUT TRANSACTION;
if ( THE INPUT INDICATES THAT THE LOOP SHOULD STOP )
{ processing = false; } // reset processing to stop loop
else { PROCESS THE TRANSACTION; }
}
and the pattern for indefinite iteration for searching a “set” of items reads
boolean item_found = false;
DETERMINE THE FIRST ‘‘ITEM’’ TO EXAMINE FROM THE SET;
while ( !item_found && ITEMS REMAIN IN THE SET TO SEARCH )
{ EXAMINE ITEM FROM THE SET;
if ( THE ITEM IS THE DESIRED ONE )
{ item_found = true; }
else { DETERMINE THE NEXT ITEM TO EXAMINE FROM THE SET; }
}

• 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

recursively defined method so that it uses a parameter whose value becomes


simpler/smaller each time the method invokes itself.

7.14 Programming Projects


1. Write these methods for comparing strings and insert them into an application
that lets a user ask questions about strings.

(a) /** palindrome decides whether a string is a palindrome, that is,


* the same sequence of letters spelled forwards or backwards.
* (e.g., "abcba" is a palindrome, but "abccda" is not.)
* @param s - the string to be analyzed
* @return whether s is a palindrome */
private static boolean palindrome(String s)
(b) /** permutation decides whether one string is a permutation of another,
* that is, the two strings contain exactly the same letters in the
* same quantities, but in possibly different orders.
* (e.g., "abcd" and "abdc" are permutations, but "abcd" and "aabdc" are not)
* @param s1 - the first string
* @param s2 - the second string
* @return whether s1 and s2 are permutations of each other */
private static boolean permutation(String s1, String s2)
(Note: your efforts will be eased if you consult the Java API for java.lang.String
and learn about the indexOf method for strings.)

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:

Type a positive integer: 18


The divisors are: 2 3 6 9
Type a positive integer: 23
The divisors are: none---it’s a prime
Type a positive integer: -1
Bye!

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

where m! is the factorial of m as seen earlier, and product(a,b) is the iterated


product from a up to b, which is defined as follows:

product(a, b) = a * (a+1) * (a+2) * ... * b

(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.)

5. Write an application that implements pattern matching on strings: The pro-


gram’s inputs are s, a string to be searched, and p, a pattern. The program
searches for the leftmost position in s where p appears as a substring. If such
a position is found, the program displays the index in s where p begins; if p
cannot be found in s, the answer displayed is -1.
Here are some hints: If you did pattern matching with pencil and paper, you
would align the pattern underneath the string and check for a match of the
alignment. For example, for string "abcdefg" and pattern "cde", you begin:

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.

6. Write a method that computes the value of PI by using this equation:

PI = 4 * ( 1 - 1/3 + 1/5 - 1/7 + ... )

The parameter to the method is the maximum value of the denominator, n,


used in the fractions, 1/n, in the series. Insert the method in a test program
and try it on various parameters and compare your results with the value of the
Java constant Math.PI.

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

Implement these equations with a method that accepts x and y as parameters


and returns as its result the greatest common divisor. Write the method with
a while-loop first. Next, write the method with recursive message sending.

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

/** format formats an integer for display.


* @param formatcode - the format in which the integer should print.
* The variants are: "n" - format the integer to have length n characters
* "n.m" - format the integer with length n characters
* followed by a decimal point, followed by m zeroes.
* (Pad with leading blanks if the integer needs less than n characters.)
* Note: if the integer is too large to fit within the formatcode, the
* formatcode is ignored and the entire integer is used.
* @param i - the integer to be formatted
* @return a string containing i, formatted according to the formatcode */
public String format(String formatcode, int i)

The second method is also called format and uses the following interface:
7.14. PROGRAMMING PROJECTS 375

/** format formats a double for display.


* @param formatcode - the format in which the double should print.
* The format must have form "n1.n2" -- format the double using n1
* characters for the whole number part, followed by a decimal point,
* followed by n2 characters for the fraction part.
* (Pad with leading blanks if the whole part of the double needs less than
* n1 characters.) Note: if the whole number part of the double is too large
* to fit within formatcode, the entire whole number part is included.
* @param d - the double to be formatted
* @return a string containing d, formatted according to the formatcode */
public void printf(String formatcode, double d)

Insert the two methods in a class called Formatter and use the class to print
nicely formatted output, e.g.,

Formatter f = new Formatter();


int i = ... read an input integer ...;
System.out.println("For input, " + f.format("3", i));
System.out.println("The square root is " + f.format("2.3", Math.sqrt(i)));

Don’t forget to print a leading negative sign for negative numbers.

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.

The egg’s fall is calculated by this famous formula from physics:

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.)

18. Program a simulation of traffic flow at a traffic intersection:

|
| 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.

Which arrangement of stop signs, if any, caused optimal traffic flow?

7.15 Beyond the Basics


7.15.1 Loop termination with break

7.15.2 The do-while Loop

7.15.3 Loop Invariants

7.15.4 Loop Termination

7.15.5 More Applets

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

7.15.1 Loop Termination with break

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.

7.15.2 The do-while Loop


The Java language provides a variant of while-loop that performs its test at the end
of the loop’s body, rather than the beginning; the new loop is a do-while-statement:
do { S } while ( E );

and its semantics states


1. The body, S, executes.
2. The test, E, computes to a boolean answer. If false, the loop is finished; if
true, Steps 1 and 2 repeat.
The do-while-statement can be convenient for programming repetitive tasks where
there is at least one repetition. Input-transition processing is one such example, and
its pattern can be written with a do-while-statement:
boolean processing = true;
do { READ AN INPUT TRANSACTION;
if ( THE INPUT INDICATES THAT THE LOOP SHOULD STOP )
{ processing = false; }
else { PROCESS THE TRANSACTION; }
}
while ( processing );
7.15. BEYOND THE BASICS 381

7.15.3 Loop Invariants


The two crucial questions of loop design are:
1. What is the purpose of the loop’s body?

2. How does the loop’s test ensure termination?


We study the first question in this section and the second in the next.
A loop must achieve its goal in multiple steps, and surprisingly, one understands
a loop better by asking about what the loop accomplishes in its steps towards its
goal, rather than asking about the ultimate goal itself. Precisely, one must answer
the following question, ideally in one sentence:
Say that the loop has been iterating for some time; what has it accomplished so far?
The answer to this question is called a loop’s invariant.
The term “invariant” is used to describe the answer, because the answer must
be a property that holds true whether the loop has executed for 2 repetitions, or 20
repetitions, or 200 repetitions, or even 0 repetitions—the answer must be invariant
of the number of repetitions of the loop.
A loop invariant also reveals what the loop’s ultimate goal will be: Once the loop
terminates, it will be the case that (i) the invariant is still true, and (ii) the loop’s
test has evaluated to false. These two facts constitute the loop’s total achievement.
Let’s consider several examples. Here is a loop:
int n = ...; // read integer from user
int i = 0;
while ( i != (n + 1) )
{ System.out.println(i * i);
i = i + 1;
}

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:

int n = ... ; // read input value


int total = 0; int i = 0;
while ( i != n )
// proposed invariant: total == 0 + 1 + ...up to... + i
{ i = i + 1;
total = total + i;
}

How do we verify that the invariant is stated correctly? To prove an invariant, we


have two tasks:

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.

2. Next, we assume the invariant is true at the start of an arbitary iteration:

total_start == 0 + 1 + ...up to... + i_start


7.15. BEYOND THE BASICS 383

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:

total_next == 0 + 1 + ...up to... + i_next

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:

• Because of i = i + 1, we know that i next == i start + 1, and because


of total = total + i, we know that total next = total start + i next
• Use algebra to add i next to both sides of the starting invariant: total start
+ i next == (0+1+...up to...+ i start) + i next.
• Using the definitions of total next and i next, we substitute and conclude
that total next == 0+1+...up to...+ i next

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

int i = INITIAL VALUE;


while ( TEST ON i )
// all elements in the range INITIAL VALUE ...up to... (i-1)
// have been combined into a partial answer
{ EXECUTE LOOP BODY;
i = i + 1;
}

In the case of input processing, the natural invariant for the transaction-processing
loop is simply,

boolean processing = true;


while ( processing )
// every transaction read so far has been processed correctly
{ READ AN INPUT TRANSACTION;
if ( THE INPUT INDICATES THAT THE LOOP SHOULD STOP )
{ processing = false; }
else { PROCESS THE TRANSACTION; }
}

Searching loops often have invariants in two parts, because the loops can exit in
two different ways; the general format is
384

boolean item_found = false;


DETERMINE THE FIRST ‘‘ITEM’’ TO EXAMINE FROM THE COLLECTION;
while ( !item_found && ITEMS REMAIN IN THE COLLECTION TO SEARCH )
// Clause 1: item_found == false implies the DESIRED ITEM is not found
// in that part of the COLLECTION examined so far
// Clause 2: item_found == true implies the DESIRED ITEM is ITEM
{ EXAMINE ITEM FROM THE COLLECTION;
if ( ITEM IS THE DESIRED ONE )
{ item_found = true; }
else { DETERMINE THE NEXT ITEM TO EXAMINE FROM THE COLLECTION; }
}

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:

int a = ...; int b = ...;


int i = b; int answer = 0;
while ( !(i == 0 ) )
{ answer = answer + a;
i = i - 1;
}
System.out.println(answer);

2. Division:

int n = ...; int d = ...;


if ( n >= 0 && d > 0 )
{ int q = 0;
int rem = n;
while ( rem >= d )
{ q = q + 1;
rem = rem - d;
}
}

3. Exponentiation:
386

int n = ...; int p = ...;


if ( p >= 0 )
{ int i = 0;
int total = 1;
while ( i != p )
{ total = total * n;
i = i+1;
}
}
else { ... };

4. Search for character, ’A’:

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;
}

7.15.4 Loop Termination


How do we write the test of a loop so that it ensures that the loop must terminate?
Imagine that every loop comes with an “alarm clock” that ticks downwards towards
0—when the clock reaches 0, the loop must terminate. (The loop might terminate
before the alarm reaches 0, but it cannot execute further once the alarm reaches 0.)
Our job is to show that a loop indeed has such an “alarm clock.”
Usually, the alarm clock is stated as an arithmetic expression, called a termina-
tion expression. Each time the loop executes one more iteration, the value of the
termination expression must decrease, meaning that eventually it must reach zero.
Consider the loop that multiplies nonnegative integers a and b:
7.15. BEYOND THE BASICS 387

int i = 0; int answer = 0;


while ( i != a )
{ answer = answer + b;
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.

7.15.5 More Applets


Part of the fun in writing animations and drawing recursive pictures is displaying
them. As noted at the end of Chapter 4, we can use an applet to display an output-
view object within a web page.
Because they are designed for use only with graphical-user-interface (GUI) con-
trollers,
Begin Footnote: We study these in Chapter 10. End Footnote
Java applets do not execute the controller objects we have written so far. So, if we
wish to display this Chapter’s moving-ball animation as an applet, we must rewrite
the controller in Figure 16.
First, we review the key modifications to make an output-view class into a Java
applet:
388

Figure 7.22: output-view class for animation applet


import java.awt.*;
import javax.swing.*;
/** AnimationApplet contains the start-up class, controller,
and output-view of the moving ball animation. */
public class AnimationApplet extends JApplet
{ private BoxWriter box writer; // the output-view of the box
private BallWriter ball writer; // the output-view of the ball in the box

private MovingBall ball; // the (address of the) moving ball object

// this was formerly the application’s main method:


/** init initializes (constructs) the applet */
public void init()
{ // construct the model objects:
int box size = 200;
int balls radius = 6;
Box box = new Box(box size);
ball = new MovingBall((int)(box size / 3.0), (int)(box size / 5.0),
balls radius, box);
// construct the output-view objects:
ball writer = new BallWriter(ball, Color.red);
box writer = new BoxWriter(box);
// constructor method for class AnimationWriter is unneeded:
// AnimationWriter writer
// = new AnimationWriter(box writer, ball writer, box size);
// the controller’s runAnimation method is renamed paint, so
// following statement is unneeded:
// new BounceController(ball, writer).runAnimation();
}

/** paint paints the applet---this is the runAnimation method.


* @param g - the graphics pen */
public void paint(Graphics g)
{ while ( true )
{ delay(20);
ball.move();
// System.out.println(ball.xPosition() + ", " + ball.yPosition());
// the following statement replaces writer.repaint():
paintAnimation(g);
}
}
...
7.15. BEYOND THE BASICS 389

Figure 7.22: output-view class for animation applet (concl.)


/** delay pauses execution for how long milliseconds */
private void delay(int how long)
{ try { Thread.sleep(how long); }
catch (InterruptedException e) { }
}

/** paintAnimation is formerly AnimationWriter’s paintComponent method */


public void paintAnimation(Graphics g)
{ box writer.paint(g);
ball writer.paint(g);
}
}

• Change the class from extends JPanel to extends JApplet.

• 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.)

• Rename paintComponent to paint.


For example, the class in Figure 20 is revised this way:
import java.awt.*;
import javax.swing.*;
/** RecursivePictureApplet displays a recursively defined picture of an
* egg in front of a picture (of eggs). */
public class RecursivePictureApplet extends JApplet
{ private double scale = 0.8; // the size of the background picture relative
// to the foreground
private int width = 400; // frame width and depth
private int depth = 200;
private int first_egg = 150; // size of the egg in the foreground

/** the constructor method is renamed init: */


public void init()
{ setBackground(Color.white); }
390

/** paintComponent is renamed paint: */


public void paint(Graphics g)
{ ... }

... the other methods stay the same ...


}

As noted in Chapter 4, the applet is executed by the applet command in an


HTML-coded web page. Here is a sample web-page:
<body>
Here is my applet:
<p>
<applet code = "RecursivePictureApplet.class" width=400 height=200>
Comments about the applet go here.<applet>
</body>

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

Data Structure: Arrays


8.1 Why We Need Arrays

8.2 Collecting Input Data in Arrays

8.3 Translation Tables

8.4 Internal Structure of One-Dimensional Arrays

8.5 Arrays of Objects

8.6 Case Study: Databases

8.6.1 Behaviors

8.6.2 Architecture

8.6.3 Specifications

8.6.4 Implementation

8.6.5 Forms of Records and Keys

8.7 Case Study: Playing Pieces for Card Games

8.8 Two-Dimensional Arrays

8.9 Internal Structure of Two-Dimensional Arrays

8.10 Case Study: Slide-Puzzle Game

8.11 Testing Programs with Arrays

8.12 Summary

8.13 Programming Projects

8.14 Beyond the Basics

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

• use arrays to model real-life collections like a library’s catalog, a company’s


database of customer records, or the playing pieces of a game.

• understand when to use one-dimensional arrays to model sequences and when to


use two-dimensional arrays to model grids.

8.1 Why We Need Arrays


When a program manipulates many variables that contain “similar” forms of data,
organizational problems quickly arise. Here is an example: In an ice-skaking compe-
tition, each skater’s performance is judged by six judges, who assign fractional scores.
The six scores must be collected and manipulated in various ways, e.g., printed from
highest to lowest, averaged, multiplied by weighting factors, and so on.
Say that the six scores are saved in these variables,
double score0; double score1; double score2;
double score3; double score4; double score5;

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,

double high score = score0 ;


for ( int i = 1; i <= 5; i = i + 1 )
{ if ( scorei > high score )
{ high score = scorei ; }
}
System.out.println(high score);
8.1. WHY WE NEED ARRAYS 393

which would examine all six variables.


Java uses array variables, for indexing like this. An array variable names a collec-
tion of individual variables, each of which possesses the same data type. For example,
we can declare and initialize an array variable that holds six doubles by stating the
following:

double[] score = new double[6];

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;

If the skater’s next score is 4.4, then we might write,

score[1] = 4.4;
394

Here is a picture that shows what we have accomplished:


0 1 2 3 4 5
score 5.9 4.4 0.0 0.0 0.0 0.0

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

If variable i held 0 (or a negative number), then score[i - 1] would be a nonsen-


sical reference, and the execution would halt with a run-time exception, called an
ArrayIndexOutOfBoundsException.
8.1. WHY WE NEED ARRAYS 395

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";

Array word is declared so that it keeps strings in its elements.


Second, if we have written a class, say, class BankAccount, then we can declare
an array to hold objects constructed from the class:
BankAccount[] r = new BankAccount[10];
r[3] = new BankAccount();
BankAccount x = new BankAccount();
r[0] = x;

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 bank’s customer accounts

• a library’s books

• playing pieces or players for an interactive game

• a table of logarithms or solutions to an algebraic equation

• 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:

int r = new int[4];

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

(a) for ( int i = 0; i < 4; i = i + 1 )


{ System.out.println(r[i]); }
(b) int i = 1;
r[i] = 10;
r[i + 2] = r[i] + 2;
for ( int i = 0; i < 4; i = i + 1 )
{ System.out.println(r[i]); }
(c) for ( int i = 3; i >= 0; i = i - 1 )
{ r[i] = i * 2; }
for ( int i = 0; i < 4; i = i + 1 )
{ System.out.println(r[i]); }
(d) r[0] = 10;
for ( int i = 1; i != 4; i = i + 1 )
{ r[i] = r[i - 1] * 2; }
for ( int i = 0; i < 4; i = i + 1 )
{ System.out.println(r[i]); }

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.

4. Declare an array, reciprocals, that holds 10 doubles; write a for-loop that


places into reciprocals[i] the value of 1.0 / i, for all values of i in the range,
1 to 9. (What value remains in reciprocals[0]?)

8.2 Collecting Input Data in Arrays


Arrays are particularly useful for collecting input data that arrive in random order.
A good example is vote counting: Perhaps you must write a program that tallies the
votes of a four-candidate election. (For simplicity, we will say that the candidates’
“names” are Candidate 0, Candidate 1, Candidate 2, and Candidate 3. ) Votes arrive
one at a time, where a vote for Candidate i is denoted by the number, i. For example,
two votes for Candidate 3 followed by one vote for Candidate 0 would appear:

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

Figure 8.1: vote counting


import javax.swing.*;
/** VoteCount tallies the votes for election candidates.
* input: a sequence of votes, terminated by a -1
* output: the listing of the candidates and their tallied votes */
public class VoteCount
{ public static void main(String[] args)
{ int num candidates = 4; // how many candidates
int[] votes = new int[num candidates]; // holds the votes;
// recall that each element is initialized to 0
// collect the votes:
boolean processing = true;
while ( processing )
// invariant: all votes read have been tallied in array votes
{ int v = new Integer(JOptionPane.showInputDialog
("Vote for (0,1,2,3):")).intValue();
if ( v >= 0 && v < votes.length ) // is it a legal vote?
{ votes[v] = votes[v] + 1; }
else { processing = false; } // quit if there is an illegal vote
}
// print the totals:
for ( int i = 0; i != votes.length; i = i + 1 )
// totals for votes[0]..votes[i-1] have been printed
{ System.out.println("Candidate" + i + " has " + votes[i] + " votes"); }
}
}

Perhaps the key statement in the program is the conditional statement,

if ( v >= 0 && v < votes.length )


{ votes[v] = votes[v] + 1; }
else { ... }

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:...)

because there is no element, votes[7]. It is always best to include conditional state-


ments that help a program defend itself against possible invalid array indexes.
8.3. TRANSLATION TABLES 399

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.

8.3 Translation Tables


Arrays are useful for representing tables of information that must be frequently con-
sulted. Here is an example: A simple form of coding is a substitution code, which
systematically substitutes individual letters by integer codes. For example, if we re-
place every blank space by 0, every ’a’ by 1, every ’b’ by 2, and so on, we are using
a substitution code. A sentence like,
a bed is read

is encoded into this sequence of integers:


1 0 2 5 4 0 9 19 0 18 5 1 4

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

For example, with a starting seed of 7, a bed is read encodes to


400

15 7 31 255 127 7 4095 4194303 7 2097151 255 15 127


The encoding program will work most efficiently if it first calculates the integer
codes for all the letters and saves them in a translation table—an array. Then, the
letters in the input words are quickly encoded by consulting the table for the codes.
We build the table as follows. First we declare the array:
int[] code = new int[27]; // this is the translation table:
// code[0] holds the code for ’ ’,
// code[1] holds the code for ’a’,
// code[2] holds the code for ’b’, and so on
Next, we systematically compute the codes to store in array code: the value of code[i]
is defined in terms of its predecessor, code[i - 1], when i is positive:
code[i] = (code[i - 1] * 2) + 1;
The arithmetic expression, i - 1, can be used, because Java allows integer-valued
expressions as indexes.
We now write this loop to compute the codes:
int seed = ... ;
code[0] = seed;
for ( int i = 1; i != code.length; i = i + 1 )
{ code[i] = (code[i - 1] * 2) + 1; }
We are now ready to read a string and translate its characters one by one into
integer codes: Java treats characters like they are integers, which makes it easy to
check if a character, c, is a lower-case letter (c >= ’a’ && c <= ’z’ does this) and to
convert the character into the correct index for array code ((c - ’a’) + 1 does this):
String input_line = JOptionPane.showInputDialog("type sentence to encode: ");
for ( int j = 0; j != input_line.length(); j = j + 1 )
{ char c = input_line.charAt(j);
if ( c == ’ ’ )
{ System.out.println(code[0]); }
else if ( c >= ’a’ && c <= ’z’ )
{ int index = (c - ’a’) + 1;
System.out.println(code[index]);
}
else { System.out.println("error: bad input character"); }
}
(Recall that S.length() returns the length of string S and that S.charAt(j) extracts
the jth character from S.)
When we build a translation table, we compute each possible translation exactly
once, and we save and reuse the answers when we read the inputs. For this reason,
translation tables are most useful when it is expensive to compute translations and
there are many inputs to translate.
8.3. TRANSLATION TABLES 401

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

for all values of i in the range, 0 to 9.


4. Translation tables have a special connection to recursively defined equations,
like the ones we saw in Chapter 7. For example, this recursive definition of
summation:

summation(0) = 0
summation(n) = n + summation(n-1), if n > 0

“defines” the following translation table:

int[] summation = new int[...];


summation[0] = 0;
for ( int n = 1; n != summation.length; n = n + 1 )
{ summation[n] = n + summation[n-1]; }

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

8.4 Internal Structure of One-Dimensional Arrays


Now that we have some experience with arrays, we should learn about their internal
structure. The story starts innocently enough: A Java array variable is declared like
any Java variable, e.g.,
int[] r;

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];

As a result of the initialization to r, computer storage looks like this:


a1 : int[6]
int[ ] r == a1 0 1 2 3 4 5
0 0 0 0 0 0

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

Once constructed, an array object’s length cannot change. The length of an


array, like r, can be obtained by the phrase, r.length. Note that r.length lacks a
parentheses set, (). For whatever reason, the Java designers made length a “public
field” of an array object, rather than a “public method.”
The array named r is called one dimensional because one index (subscript) is
required to identify a specific element in the array. Some programming languages
permit construction of a two-dimensional array (also known as a matrix or grid),
where two indexes are required to identify an element. Two dimensional arrays are
studied later in this Chapter.
We can construct arrays of integers, doubles, booleans, strings, and indeed, of any
legal data type. Here are some examples:
• double[] score = new double[6] constructs an array of six elements, each of
which holds doubles. Each element is initialized to 0.0.
• boolean[] safety check = new boolean[i + 2] constructs an array whose length
is the value of integer variable i plus 2. The example shows that an integer-
valued arithmetic expression states the length of the array object. (The value
must be nonnegative.) Each element of a boolean array is initialized to false
• BankAccount[] account = BankAccount[100] constructs an array that can hold
100 distinct BankAccount objects. (See Figure 11, Chapter 6, for class BankAccount.)
Each element is initialized to null, which means, “no value.”
The last statement of the last example is crucial to understanding Java arrays:
When you construct an array whose elements hold objects, the array’s elements are
initialized to null values—there is only a “container” but no objects in it. Therefore,
you must explicitly construct new objects and assign them to the elements. For the
last example, we might write a loop that constructs and inserts objects into array
account:

BankAccont[] account = new BankAccount[100];


for ( int i = 0; i != account.length; i = i + 1 )
{ account[i] = new BankAccount( ... ); } // see Figure 11, Chapter 6

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

As noted in the previous section, integer-valued arithmetic expressions are used to


index an array’s elements. For example, these statements make short work of building
a lookup table of powers of two:
int[] r = new int[6];
r[0] = 1;
for ( int i = 1; i < r.length; i = i + 1 )
{ r[i] = r[i - 1] * 2; }

Now, r[j] holds the value of 2j :


0 1 2 3 4 5
1 2 4 8 16 32

A numeric or boolean array can be constructed and initialized with a set-like


notation, which looks like this:
int[] r = {1, 2, 4, 8, 16, 32};

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;
}

When this method is invoked, for example,


double[] numbers d = {2.3, -4.6, 8, 3.14};
double[] e = reverse(d);

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:

public static void main(String[] args)


{ ... args[0] ... args[1] ... }
8.4. INTERNAL STRUCTURE OF ONE-DIMENSIONAL ARRAYS 405

When an application is started, whatever program arguments supplied in the appli-


cation’s start-up command are packaged into an array object, and the address of the
array is bound to args. This explains why the leading program argument is referred
to as args[0], the next argument is args[1], and so on. The number of program
arguments supplied is of course args.length.

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].

4. Write this method:

/** maxElement returns the largest integer in its array parameter.


* @param r - an array of 1 or more integers
* @return the largest integer in the array */
public int maxElement(int[] r)

5. Write this method:

/** add adds the elements of two arrays, element-wise


* @param r1 - an array
* @param r2 - an array. Note: the two arrays’ lengths must be the same
* @return a newly constructed array, s, such that s[i] = r1[i] + r2[i],
* for all i; return null, if r1 and r2 have different lengths. */
public double[] add (double[] r1, double[] r2)

6. Given this declaration,

BankAccount[] bank = new BankAccount[100];


406

(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];

8.5 Arrays of Objects


The previous section pointed out that an array can hold objects. Arrays of objects
can collect together bank accounts or library books or tax records into a single “data
base.” Here is a small example.
Recall once more, class BankAccount, from Figure 11, Chapter 6. When we
construct a new BankAccount(N), we construct an object with an initial balance of N
that can receive deposits and withdrawals. For example,
BankAccount x = new BankAccount(70);
... x.withdraw(50) ...

constructs an account named x with an initial balance of 70 and performs a withdrawal


of 50 upon it.
A bank has hundreds, if not thousands, of customers, so a program that maintains
the bank’s accounts must construct many BankAccount objects. An array is a good
structure for saving the objects. Here is one simple modelling: Say that a bank is able
to maintain at most 100 accounts. Each account is given an identification number
in the range of 0 to 99. The program that does accounting for the bank will use an
array like this:
BankAccount[] bank = new BankAccount[100];

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

Or, more directly stated,


bank[75] = new BankAccount(200);

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

. . .

If the customer wishes to withdraw 60 from the account, this invocation,


bank[75].withdraw(60)

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

Of course, it is overly simplistic to use simple integers as identification numbers


for bank accounts—we should use a more general notion of a key to identify an
account. The key might be a word, or a sequence of numerals and letters, or even an
object itself. The case study in the next section develops a more realistic approach
to maintaining databases of objects where objects are identified by keys.

Exercises
1. Here is a class, Counter, that can remember a count:

public class Counter


{ private int c;

public Counter(int v) { c = v; }
public void increment() { c = c + 1; }
public int getCount() { return c; }
}

Say that we change the declaration of votes in the vote-counting application in


Figure 1 to be

Counter[] votes = new Counter[num_candidates];

Revise the vote-counting application to operate with this array.

2. Let’s write a class that “models” the bank described in this section:

/** Bank models a collection of bank accounts */


public class Bank
{ BankAccount[] bank; // the array that holds the account
int max_account; // the maximum account number

/** Constructor Bank initialize the bank


* @param how_many - the maximum number of bank accounts */
public Bank(int how_many)
{ max_account = how_many;
bank = new BankAccount[how_many];
}

/** addNewAccount adds a new account to the bank


* @param id_number - the account’s identification number; must be in
* the range, 0..maximum_account_number - 1
* @param account - the new bank account object
* @return true, if the account is succesfully added;
8.6. CASE STUDY: DATABASES 409

* return false, if the id_number is illegal */


public boolean addNewAccount(int id_number, BankAccount account)
{ boolean result = false;
if ( id_number >= 0 && id_number < max_account
&& bank[id_number] == null ) // is id_number legal?
{ bank[id_number] = account;
result = true;
}
return result;
}

/** getAccount finds a bank account


* @param id_number - the identification number of the desired account
* @return the bank account whose identification is id_number;
* if there is no account with the id_number, return null */
public BankAccount getAccount(int id_number)
{ ... }

/** deleteAccount removes a bank account


* @param id_number - the identification number of the account to be removed
* @return true, if the deletion was successful;
* return false, if no account has the id_number */
public boolean deleteAccount(int id_number)
{ ... }
}

We might use the class as follows:

Bank b = new Bank(500);


b.addNewAccount(155, new BankAccount(100));
...
BankAccount a = b.getAccount(155);
... a.deposit(200) ...

Write the two missing methods for class Bank.


3. Use class Bank in the previous exercise to write an application that lets a user
construct new bank accounts, do deposits and withdrawals, and print balances.

8.6 Case Study: Databases


A large collection of information, such as a company’s sales records or its customer
accounts or its payroll information, is called a database. An important programming
challenge is determining the proper structure for a database.
410

In simplest terms, a database is a “container” into which objects are inserted,


located, and removed; the objects that are stored in a database are called records.
An important feature about a record is that it is uniquely identified by its key, which
is held within the record itself. Here are some examples of records:

• A bank’s database holds records of accounts. Each account record is uniquely


identified by a multi-letter-and-digit account number. A typical record would
contain information such as

1. its key, the multi-digit integer, which identifies the account


2. the name (or some other identification) of the account’s owner
3. the amount of money held in the account

• 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:

1. the key, which is the book’s catalog number


2. the book’s title, author, publisher, and publication date
3. whether or not the book is borrowed, and if so, by which patron

• 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.

Compare this concept to that of an array: An array is a container that locates


objects by using integer indices numbered 0, 1, 2, ..., and so on. A database is like a
“smart array” that uses a record’s key to save and locate the record.
How can we model and build a general-purpose database in Java? Here are some
crucial concepts:

1. keys are objects

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

3. a database is a kind of “array” of record objects; it must have methods for


inserting a record, finding a record, and deleting a record

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

object. The remaining structure of the Records is unimportant and unknown


to the database.

2. The Database will possess insert, find, and delete methods.

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.

2. Select an architecture for the subassembly.

3. For each of the architecture’s components, specify classes with appropriate at-
tributes and methods.

4. Write and test the individual classes.

5. Integrate the classes into a complete subassembly.

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

Figure 8.2: architecture for a database

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

Figure 8.3: specifications for database building

Database a container for data items, called Records


Attribute
private Record[] base Holds the records inserted into the database.
Methods
insert(Record r): boolean Attempts to insert the record, r, into the database.
Returns true if the record is successfully added,
false otherwise.
find(Key k): Record Attempts to locate the record whose key has value
k. If successful, the address of the record is re-
turned, otherwise, null is returned.
delete(Key k): boolean Deletes the record whose key has value k. If suc-
cessful, true is returned; if no record has key k,
false is returned.

Record a data item that can be stored in a database


Methods
getKey(): Key Returns the key that uniquely identifies the record.

Key an identification, or “key,” value


Methods
equals(Key m): boolean Compares itself to another key, m, for equality. If
this key and m are same key value, then true is
returned; if m is a different key value, then false is
returned.

The specification for Record is kept as minimal as possible: whatever a record


object might be, it has a function, getKey, that returns the key that uniquely identifies
the record. Similarly, it is unimportant whether a key is a number or a string or
whatever else; therefore, we require only that a key possesses a method, equals, that
checks the equality of itself to another key.
Table 3 presents the specifications that summarize our assumptions about databases,
records, and keys. Because we have provided partial (incomplete) specifications for
Record and Key, many different classes might implement the two specifications. For
example, we might write class Book to implement a Record so that we can build a
database of books, or we might write class BankAccount to implement a database of
bank accounts. Different classes of keys might also be written, if only because books
use different keys than do bank accounts.
Key’s specification deserves a close look: the specification is written as if keys are
objects (and not mere ints). For this reason, given two Key objects, K1 and K2, we
8.6. CASE STUDY: DATABASES 415

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:

private Record[] base;

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).

3. Insert r into the empty element.


Each of the algorithm’s three steps requires more refinement: To fill in details in the
first step, say that we write a helper method, findLocation, which searches the array
for a record whose key equals k. The helper method might be specified like this:
/** findLocation is a helper method that searches base for a record
* whose key is k. If found, the array index of the record within
* base is returned, else -1 is returned. */
private int findLocation(Key k)

Then, Step 1 of the algorithm is merely,


if ( findLocation(r.getKey()) == -1 )

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

boolean found_empty_place = false;


int i = 0;
while ( !found_empty_place && i != base.length )
// so far, all of base[0]..base[i-1] are occupied
{ if ( base[i] == null ) // is this element empty?
{ found_empty_place = true; }
else { i = i + 1; }
}

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:

// This constructs a new array twice as large as base:


Record[] temp = new Record[base.length * 2];
// Copy elements in array named by base into temp:
for ( int j = 0; j != base.length; j = j + 1 )
{ temp[j] = base[j]; }
// Change base to hold address of temp:
base = temp;

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,

1. Search array base to see if if a record with the key, k, is present.

2. If such a record is located, say, at element index, then delete it by assigning,


base[index] = null.

We use the helper method, findLocation, to code Step 1. We have this coding:

int index = findLocation(k);


if ( index != -1 )
{ base[index] = null; }
8.6. CASE STUDY: DATABASES 417

See Figure 4 for the completed method.


We can write the lookup method so that it merely asks findLocation to find the
desired record in the array. Again, see Figure 4.
To finish, we must write the findLocation method, which finds the record in
array base whose key is k. The algorithm is a standard searching loop, but there is a
small complication, because array base might have null values appearing in arbitrary
places, due to deletions of previously inserted records:
private int locationOf(Key k)
{ int result = -1; // recall that -1 means ‘‘not found’’
boolean found = false;
int i = 0;
while ( !found && i != base.length )
{ if ( base[i] != null // is this element occupied?
&& base[i].keyOf().equals(k) ) // is it the desired record?
{ found = true;
result = i;
}
else { i = i + 1; }
}
return result; // return array index of the record found
}
Note the conditional statement in the loop’s body:
if ( base[i] != null // is this array element occupied?
&& base[i].keyOf().equals(k) ) // is it the desired record?
{ ... } // we found the record at array element, i
else { i = i + 1; } // the record is not yet found; try i + 1 next

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

Figure 8.4: class Database


/** Database implements a database of records */
public class Database
{ private Record[] base; // the collection of records
private int NOT FOUND = -1; // int used to denote when a record not found

/** Constructor Database initializes the database


* @param initial size - the size of the database */
public Database(int initial size)
{ if ( initial size > 0 )
{ base = new Record[initial size]; }
else { base = new Record[1]; }
}

/** findLocation is a helper method that searches base for a record


* whose key is k. If found, the index of the record is returned,
* else NOT FOUND is returned. */
private int findLocation(Key k)
{ int result = NOT FOUND;
boolean found = false;
int i = 0;
while ( !found && i != base.length )
{ if ( base[i] != null && base[i].keyOf().equals(k) )
{ found = true;
result = i;
}
else { i = i + 1; }
}
return result;
}

/** find locates a record in the database based on a key


* @param key - the key of the desired record
* @return (the address of) the desired record;
* return null if record not found. */
public Record find(Key k)
{ Record answer = null;
int index = findLocation(k);
if ( index != NOT FOUND )
{ answer = base[index]; }
return answer;
}
...
8.6. CASE STUDY: DATABASES 419

Figure 8.4: class Database (concl.)


/** insert inserts a new record into the database.
* @param r - the record
* @return true, if record added; return false if record not added because
* another record with the same key already exists in the database */
public boolean insert(Record r)
{ boolean success = false;
if ( findLocation(r.keyOf()) == NOT FOUND ) // r not already in base?
{ // find an empty element in base for insertion of r:
boolean found empty place = false;
int i = 0;
while ( !found empty place && i != base.length )
// so far, all of base[0]..base[i-1] are occupied
{ if ( base[i] == null ) // is this element empty?
{ found empty place = true; }
else { i = i + 1; }
}
if ( found empty place )
{ base[i] = r; }
else { // array is full! So, create a new one to hold more records:
Record[] temp = new Record[base.length * 2];
for ( int j = 0; j != base.length; j = j + 1 )
{ temp[j] = base[j]; } // copy base into temp
temp[base.length] = r; // insert r in first free element
base = temp; // change base to hold address of temp
}
success = true;
}
return success;
}

/** delete removes a record in the database based on a key


* @param key - the record’s key (identification)
* @return true, if record is found and deleted; return false otherwise */
public boolean delete(Key k)
{ boolean result = false;
int index = findLocation(k);
if ( index != NOT FOUND )
{ base[index] = null;
result = true;
}
return result;
}
}
420

• 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.

8.6.5 Forms of Records and Keys


When we use class Database to hold records, we must write a class Record and a
class Key. The contents of these classes depends of course on the application that
requires the database, but we know from Table 3 that class Record must include a
getKey method and class Key must include an equals methods. Figure 5 shows one
such implementation: a record that models a simple bank account and a key that is
merely a single integer value.
The Record in Figure 5 has additional methods that let us do deposits and check
balances of a bank account, but the all-important getKey method is present, meaning
that the record can be used with class Database of Figure 4.
In order to conform to the requirements demanded by class Database, the integer
key must be embedded within a class Key. This means the integer is saved as a
private field within class Key and that the equals method must be written so that
it asks another key for its integer attribute, by means of an extra method, getInt.
Here is how we might use the classes in Figure 5 in combination with Figure 4.
Perhaps we are modelling a bank, and we require this database:

Database bank = new Database(1000);

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:

int i = ...some integer selected by the customer...;


int start_balance = ...some initial deposit by the customer...;
Key k1 = new Key(i);
boolean success = bank.insert( new Record(start_balance, k1) );
System.out.println("account inserted = " + success);

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:

Record r = bank.find(k1); // recall that k1 is the account’s key


if ( r != null ) // did we successfully fetch the account?
{ System.out.println(r.getBalance()); }

To show that the database can be used in a completely different application, we


find in Figure 6 a new coding of record and key, this time for library books. Now,
class Record holds attributes for a book’s title, author, publication date, and catalog
number; the catalog number serves as the book’s key.
8.6. CASE STUDY: DATABASES 421

Figure 8.5: BankAccount Record and AccountKey


/** Record models a bank account with an identification key */
public class Record
{ private int balance; // the account’s balance
private Key id; // the identification key

/** Constructor Record initializes the account


* @param initial amount - the starting account balance, a nonnegative.
* @param id - the account’s identification key */
public Record(int initial amount, Key id)
{ balance = initial amount;
key = id;
}

/** deposit adds money to the account.


* @param amount - the amount of money to be added, a nonnegative int */
public void deposit(int amount)
{ balance = balance + amount; }

/** getBalance reports the current account balance


* @return the balance */
public int getBalance() { return balance; }

/** getKey returns the account’s key


* @return the key */
public int getKey() { return key; }
}

/** Key models an integer key */


public class Key
{ private int k; // the integer key

/** Constructor Key constructs the Key


* @param i - the integer that uniquely defines the key */
public Key(int i) { k = i; }

/** equals compares this Key to another for equality


* @param c - the other key
* @return true, if this key equals k’s; return false, otherwise */
public boolean equals(Key c)
{ return ( k == c.getInt() ); }

/** getInt returns the integer value held within this key */
public int getInt() { return k; }
}
422

Figure 8.6: Book Record and CatalogNumber Key


/** Record models a Library Book */
public class Record
{ // the names of the fields describe their contents:
private Key catalog number;
private String title;
private String author;
private int publication date;

/** Constructor Record constructs the book.


* @param num - the book’s catalog number
* @param a - the book’s author
* @param t - the book’s title */
public Record(Key num, String a, String t, int date)
{ catalog number = num;
title = t;
author = a;
publication date = date;
is borrowed by someone = false;
}

/** getkey returns the key that identifies the record


* @return the key */
public Key getKey() { return catalog number; }

/** getTitle returns the book’s title


* @return the title */
public String getTitle() { return title; }

/** getAuthor returns the book’s author


* @return the author */
public String getAuthor() { return author; }

/** getDate returns the book’s publication date


* @return the date */
public int getDate() { return publication date; }
}
8.6. CASE STUDY: DATABASES 423

Figure 8.6: CatalogNumber Key (concl.)


/** Key models a Library-of-Congress-style id number,
* consisting of a letter code concatenated to a decimal number */
public class Key
{ private String letter code; // the letter code, e.g., "QA"
private double number code; // the number code, e.g., 76.884

/** Constructor Key constructs a catalog number


* @param letters - the letter code, e.g., "QA"
* @param num - the decimal number code, e.g., 76.884 */
public Key(String letters, double num)
{ letter code = letters;
number code = num;
}

/** 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);

Record book = new Book( new Key("QA", 76.8), "Charles Dickens",


"Great Expectations", 1860 );
library.insert(book);

// We might locate the book this way:


Key lookup_key = new Key("QA", 76.8);
book = library.find(lookup_key);

// We can delete the book, if necessary:


boolean deleted = library.delete(lookup_key);
As noted by the statement, Key lookup key = new Key("QA", 76.8), we can manu-
facture keys as needed to perform lookups and deletions.
It is a bit unfortunate that the bank account record in Figure 5 was named class
Record and that the book record in Figure 6 was also named class Record; more
descriptive names, like class BankAccount and class Book would be far more ap-
propriate and would let us include both classes in the same application if necessary.
(Perhaps a database must store both bank accounts and books together, or perhaps
one single application must construct one database for books and another for bank
accounts.)
Of course, we were forced to use the name, class Record, for both records because
of the coding for class Database demanded it. The Java language lets us repair this
naming problem with a new construction, called a Java interface. We will return
to the database example in the next chapter and show how to use a Java interface
with class Database to resolve this difficulty.

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.

8.7 Case Study: Playing Pieces for Card Games


Computerized games must model a game’s playing pieces, the playing board, and even
the game’s players. A classic example of “playing pieces” are playing cards. Even
8.7. CASE STUDY: PLAYING PIECES FOR CARD GAMES 425

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.

Behavior of Cards and Decks


Perhaps we do not think of playing cards as having behaviors, but they are playing
pieces, and a playing piece, whether it be a chess pawn or a queen-of-hearts card, has
abilities or attributes that make it distinct from other playing pieces. A playing card
is a good example: It has a suit (diamonds, hearts, clubs, or spades) and count (ace
through ten, jack, queen, or king), e.g., hearts and queen. A card’s suit and count
are attributes and not behaviors in themselves, but a card game assigns behaviors
based on the attributes, e.g., in the card game, Blackjack, a card that has the count
of queen has the ability to score 10 points for the player who holds it.
In almost every game, playing pieces must be placed onto or into a container or
playing board—cards are collected into a container called a deck. Unlike the database
developed in the previous section, a card deck begins completely filled with cards. The
deck’s primary behavior is to surrender a card from itself whenever asked, until the
deck is finally empty.
Card games often use other forms of containers. For example, each player might
have its own personal container for cards, called a hand (of cards). A hand is initialized
so that it is empty and cards can be added to it, one by one.
For this small case study, we will design a subassembly consisting only of ordinary
playing cards and a deck to hold them. The architecture for the two classes is simple:

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

Figure 8.7: specification for class CardDeck

class CardDeck models a deck of


cards
Attribute
private Card[] container for the
deck cards left in the
deck
Methods
newCard(): Card return a card from
the deck; if the
deck is empty, re-
turn null
moreCards(): return true if more
boolean cards remain in the
deck; return false,
otherwise

Figure 8.8: specification of class Card

class Card models a playing card


Attributes
private String suit the card’s suit, e.g., spades, hearts, diamonds, clubs
private int count the card’s count, e.g., ace, 2, 3, ..., king

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:

Card c = new Card(Card.HEARTS, Card.QUEEN)

to construct a queen-of-hearts object. And, we can ask,

if ( c.getCount() == Card.QUEEN ) { ... }


8.7. CASE STUDY: PLAYING PIECES FOR CARD GAMES 427

Figure 8.9: playing card


/** Card models a playing card */
public class Card
{ // definitions that one can use to describe the value of a card:
public static final String SPADES = "spades";
public static final String HEARTS = "hearts";
public static final String DIAMONDS = "diamonds";
public static final String CLUBS = "clubs";

public static final int ACE = 1;


public static final int JACK = 11;
public static final int QUEEN = 12;
public static final int KING = 13;

public static final int SIZE OF ONE SUIT = 13; // how many cards in one suit

// These are the card’s attributes:


private String suit;
private int count;

/** Constructor Card sets the suit and count.


* @param s - the suit
* @param c - the count */
public Card(String s, int c)
{ suit = s;
count = c;
}

/** getSuit returns the card’s suit. */


public String getSuit()
{ return suit; }

/** getCount returns the card’s count. */


public int getCount()
{ return count; }
}
428

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,

if ( c.getCount() >= Card.QUEEN ) { ... }

because integers can be compared by greater-than.


As always, the keyword, public, means that the variable can be referenced by
other classes; the keyword, final, states that the variable name can not be changed
by an assignment; the value is forever constant. Finally, the keyword, static, ensures
that the variable is not copied as a field into any Card objects that are constructed
from class Card.
It is traditional to declare public static final variables with names that are all
upper-case letters.
We have used public static final variables already when in previous chapters
we used predefined Java-library values like Color.red (to paint on a graphics window)
and Calendar.DAY OF MONTH (to get the date from a Gregorian calendar object).
The remainder of class Card is simple—it contains a constructor method, which
initializes a card object’s suit and count, and two accessor methods, which return the
suit and count.
Next, we consider class CardDeck. Its attributes are

private int card_count; // how many cards remain in the deck


private Card[] deck = new Card[4 * Card.SIZE_OF_ONE_SUIT];
// invariant: elements deck[0]..deck[card_count - 1] hold cards

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

1. Randomly calculate an integer in the range of 0 to card count - 1; call it index.

2. Remove the card at element deck[index]

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].

4. Decrease card count by one.

Step 1 can be done with the built-in Java method, Math.random(), which computes a
nonnegative pseudo-random fraction less than 1.0:

int index = (int)(Math.random() * card_count);

The computation, Math.random() * card count, generates a nonnegative fractional


number that must be less than card count (why?), and the cast, (int), truncates the
factional part, leaving an integer in the range, 0 to card count - 1.
Step 3 is performed with a simple loop:

for ( int i = index + 1; i != card_count; i = i + 1 )


// so far, cards from index+1 to i-1 have been shifted left
// in the array by one position
{ deck[i - 1] = deck[i]; }

The completed version of the method is in Figure 10.

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

Figure 8.10: class CardDeck


/** CardDeck models a deck of cards. */
public class CardDeck
{
private int card count; // how many cards remain in the deck
private Card[] deck = new Card[4 * Card.SIZE OF ONE SUIT];
// invariant: elements deck[0]..deck[card count - 1] hold cards

/** 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);
}

/** newCard gets a new card from the deck.


* @return a card not used before, or return null, if no cards are left */
public Card newCard()
{ Card next card = null;
if ( card count == 0 )
{ System.out.println("CardDeck error: no more cards"); }
else { int index = (int)(Math.random() * card count); // randomly choose
next card = deck[index];
// once card is extracted from deck, shift other cards to fill gap:
for ( int i = index+1; i != card count; i = i + 1 )
// so far, cards from index+1 to i-1 have been shifted left
// in the array by one position
{ deck[i - 1] = deck[i]; }
card count = card count - 1;
}
return next card;
}

/** moreCards states whether the deck has more cards to give.
* @return whether the deck is nonempty */
public boolean moreCards()
{ return (card count > 0); }

/** createSuit creates a suit of cards for a new card deck. */


private void createSuit(String which suit)
{ for ( int i = 1; i <= Card.SIZE OF ONE SUIT; i = i + 1 )
{ deck[card count] = new Card(which suit, i);
card count = card count + 1;
}
}
}
8.8. TWO-DIMENSIONAL ARRAYS 431

class Dealer models a dealer of


cards
Responsibilities
(methods)
dealTo(Player p) gives cards, one
by one, to player
p, until p no
longer wants a
card

class Player models a player of


a card game
Responsibilities
(methods)
wantsACard(): replies as to
boolean whether another
card is desired
receiveCard(Card accepts card c and
c) adds it to its hand
showCards(): returns an array
Card[] of the cards that it
holds

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.

5. Revise the controller in the previous Exercise so that there is an array of 3


players; the dealer deals cards to each player in turn, and then all players show
their cards.

8.8 Two-Dimensional Arrays


Often, we display a collection of names and/or numbers in a table- or grid-like layout;
here is an example. Say that 4 candidates participate in a “national election,” where
the candidates compete for votes in 3 different regions of the country. (In the United
States, for example, there are 50 such regions—the 50 states of the union.) Therefore,
separate vote tallies must be kept for each of the 3 regions, and the votes are recorded
432

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;

The phrase, election[1][3], indicates the specific element in the matrix.


To give every candidate in Region 2 exactly 100 votes, we would say
for ( int i = 0; i != 4; i = i + 1 )
{ election[2][i] = 100; }

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.

2. Create a two-dimensional array of Strings, m, with 7 rows and 5 columns and


initialize it so that each m[i][j] holds the string "Element " + i + " " + j.
434

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.)

8.9 Internal Structure of Two-Dimensional Arrays


We can imagine that a two-dimensional array is a matrix and draw it as such, but the
array’s true arrangement in computer storage is more complex: A two-dimensional
array is in fact an array of arrays. To see this, reconsider

int[][] election = new int[3][4];


8.9. INTERNAL STRUCTURE OF TWO-DIMENSIONAL ARRAYS 435

Here is its layout in computer storage:

a1 : int[3] [ ]

int[ ] [ ] election == a1 0 a2
1 a3
2 a4

a2 : int[4] a3 : int[4] a4 : int[4]


0 1 2 3 0 1 2 3 0 1 2 3
0 0 0 0 0 0 0 0 0 0 0 0

In Java, a two-dimensional array is built in terms of multiple one-dimensional array


objects. The diagram shows that the object at address a1 is a one dimensional array
that holds the addresses of the matrix’s rows.
For example, when we write, election[1][3], this is computed to a1[1][3], that
is, the array object at a1 is the one that will be indexed. Since element 1 within
object a1 is a3, the indexing is computed to a3[3], which identifies element 3 in the
array object at address a3.
Fortunately, this detailed address lookup is performed automatically and is hidden
from the programmer—it is indeed safe to pretend that election is the name of a
matrix of integer elements. But sometimes the knowledge of the matrix’s underlying
structure is exposed in the code that traverses the array, as in these for-loops, which
print the total votes for each region:
for ( int i = 0; i != 3; i = i + 1 )
{ int[] region = election[i]; // region holds the address of a row
int total = 0;
for ( int j = 0; j != 4; j = j + 1 )
{ total = total + region[j]; }
System.out.println(total + " votes were cast in Region " + i);
}

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][]

double[][] ragged ==a1 0 null


1 null
2 null
3 null

The following three statements construct one-dimensional array objects and assign
them to ragged’s elements, giving us
a1 : double[4][]

double[][] ragged ==a1 0 a2


1 null
2 a3
3 a4

a2 : double[2] a3 : double[1] a4 : double[0]


0 1 0
0.0 0.0 0.0

This is an example of a “ragged array”—a two-dimensional array whose rows have


different lengths. For example, an election where different candidates are eligible in
different regions of a country might be modelled by a ragged array.
Notice that ragged[0].length equals 2, whereas ragged[2].length equals 1, whereas
ragged[1].length generates an exception (run-time error), because there is no array
of any length at the element. The array at element ragged[3] truly has length 0,
meaning that there are no elements at all that can be indexed in that row.
8.10. CASE STUDY: SLIDE-PUZZLE GAME 437

8.10 Case Study: Slide-Puzzle Game

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

Figure 8.11: architecture of model for slide puzzle

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.

Architecture, Specification, and Implementation of the Model


The application that implements the puzzle will need a model subassembly that
models the puzzle board and the pieces that move about the board; the model’s
architecture, presented in Figure 11, is simple and includes an initial specification of
the two classes that must be written.
The SlidePuzzleBoard’s sole responsibility is to move a puzzle piece when asked,
e.g., move(4) asks the board to move the piece whose face is labelled by 4.
A PuzzlePiece has only the attribute of the number on its face; it has no respon-
sibilities. For this reason, its coding is simple and appears in Figure 12.
Next, we consider writing class SlidePuzzleBoard. The class’s primary attribute
is the array that holds the (addresses of the) puzzle pieces:
private PuzzlePiece[][] board; // the array that holds the pieces

// one position on the board must be an empty space:


private int empty_row;
8.10. CASE STUDY: SLIDE-PUZZLE GAME 439

Figure 8.12: class PuzzlePiece


/** PuzzlePiece defines a slide-puzzle playing piece */
public class PuzzlePiece
{ private int face value; // the value written on the piece’s face

/** Constructor PuzzlePiece creates a piece


* @param value - the value that appears on the face of the piece */
public PuzzlePiece(int value)
{ face value = value; }

/** valueOf returns the face value of the piece */


public int valueOf()
{ return face value; }
}

private int empty_col;


// representation invariant: board[empty_row][empty_col] == null

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.

The Application’s Architecture


The model subassembly neatly fits into a standard model-view-controller architecture,
which we see in Figure 14.

Implementing the Controller


Figure 14’s class diagram specifies the methods for the controller and view compo-
nents. Consider the controller first; its play method should let the user repeatedly
enter moves to the slide puzzle. The algorithm is merely a loop, which

1. tells the PuzzleWriter component to paint the current state of the puzzle;

2. reads the next move from the user;

3. tells the SlidePuzzleBoard to attempt the move

The controller and its move method is presented in Figure 15.

Implementing the View


The output-view class, PuzzleWriter, has the responsibility of painting the con-
tents of the puzzle board in a graphics window. Because the controller, class
PuzzleController, sends its repaint requests via method, displayPuzzle, the displayPuzzle
method merely asks the view to repaint itself. The hard work of painting is done by
paintComponent, which must display the puzzle pieces as a grid. A nested for-loop
invokes the helper method, paintPiece, which paints each of the pieces, one by one.
Figure 16 shows the coding.

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

Figure 8.13: class SlidePuzzleBoard


/** SlidePuzzleBoard models a slide puzzle. */
public class SlidePuzzleBoard
{ private int size; // the board’s size
private PuzzlePiece[][] board; // the array that holds the pieces

// one position on the board must be an empty space:


private int empty row;
private int empty col;
// representation invariant: board[empty row][empty col] == null

/** Constructor SlidePuzzleBoard constructs the initial puzzle, which has


* the pieces arranged in descending numerical order.
* @param s - the size of the puzzle, a positive integer (e.g., s==4 means
* the puzzle is 4 x 4 and will have pieces numbered 15, 14, ..., 1) */
public SlidePuzzleBoard(int s)
{ size = s;
board = new PuzzlePiece[size][size];
// create the individual pieces and place on the board in reverse order:
for ( int num = 1; num != size * size; num = num + 1 )
{ PuzzlePiece p = new PuzzlePiece(num);
int row = num / size;
int col = num % size;
// set p in a ‘‘reversed position’’ on the board:
board[size - 1 - row][size - 1 - col] = p;
}
// remember the location on the board where initially there is no piece:
empty row = size - 1;
empty col = size - 1;
}

/** contents returns the current state of the puzzle


* @return a matrix that contains the addresses of the pieces */
public PuzzlePiece[][] contents()
{ PuzzlePiece[][] answer = new PuzzlePiece[size][size];
for ( int i = 0; i != size; i = i + 1 )
{ for ( int j = 0; j != size; j = j + 1 )
{ answer[i][j] = board[i][j]; }
}
return answer;
}
...
442

Figure 8.13: class SlidePuzzleBoard (concl.)


/** move moves a piece into the blank space, provided it is a legal move.
* @param w - the face value of the piece that one wishes to move
* @return true, if the piece labelled w was moved into the empty space;
* return false if the piece cannot be moved into the empty space */
public boolean move(int w)
{ int NOT FOUND = -1;
int row = NOT FOUND; // row and col will remember where piece w lives
int col = NOT FOUND;
// try to find w adjacent to the empty space on the board:
if ( found(w, empty row - 1, empty col) )
{ row = empty row - 1;
col = empty col;
}
else if ( found(w, empty row + 1, empty col) )
{ row = empty row + 1;
col = empty col;
}
else if ( found(w, empty row, empty col - 1) )
{ row = empty row;
col = empty col - 1;
}
else if ( found(w, empty row, empty col + 1) )
{ row = empty row;
col = empty col + 1;
}

if ( row != NOT FOUND )


{ // move the piece into the empty space:
board[empty row][empty col] = board[row][col];
// mark the new empty space on board:
empty row = row;
empty col = col;
board[empty row][empty col] = null;
}
return row != NOT FOUND;
}

/** 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

Figure 8.14: class diagram for slide-puzzle program

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.

2. Use the for-loops in the previous Exercise in an alternate implementation of


class PuzzleWriter that displays the puzzle in the console window.

3. Test class PuzzleWriter by creating these objects,

SlidePuzzleBoard board = new SlidePuzzleBoard(3);


PuzzleWriter writer = new PuzzleWriter(board, 3);
writer.displayPuzzle();

where you use this “dummy” class:

public class SlidePuzzleBoard


{ private int size;

public SlidePuzzleBoard(int s) { size = s; }

public PuzzlePiece[][] contents()


{ PuzzlePiece[][] answer = new PuzzlePiece[size][size];
int k = 0;
for ( int i = 0; i != size; i= i+1 )
{ for ( int j = 0; j != size; j = j+1 )
{ answer[i][j] = new PuzzlePiece(k);
444

Figure 8.15: controller for slide puzzle program


import javax.swing.*;
/** PuzzleController controls the moves of a slide puzzle */
public class PuzzleController
{ private SlidePuzzleBoard board; // model
private PuzzleWriter writer; // output view

/** Constructor PuzzleController initializes the controller


* @param b - the model, the puzzle board
* @param w - the output view */
public PuzzleController(SlidePuzzleBoard b, PuzzleWriter w)
{ board = b;
writer = w;
}

/** play lets the user play the puzzle */


public void play()
{ while ( true )
{ writer.displayPuzzle();
int i = new Integer
(JOptionPane.showInputDialog("Your move:")).intValue();
boolean good outcome = board.move(i);
if ( !good outcome )
{ writer.printError("Bad move--puzzle remains the same."); }
}
}
}

/** SlidePuzzle implements a 4 x 4 slide puzzle. Input to the program


* is a sequence of integers between 1..15. The program never terminates. */
public class SlidePuzzle
{ public static void main(String[] args)
{ int size = 4; // a 4 x 4 slide puzzle
SlidePuzzleBoard board = new SlidePuzzleBoard(size);
PuzzleWriter writer = new PuzzleWriter(board, size);
PuzzleController controller = new PuzzleController(board, writer);
controller.play();
}
}
8.10. CASE STUDY: SLIDE-PUZZLE GAME 445

Figure 8.16: output-view class for puzzle game


import java.awt.*; import javax.swing.*;
/** PuzzleWriter displays the contents of a slide puzzle */
public class PuzzleWriter extends JPanel
{ private SlidePuzzleBoard board; // the board that is displayed
private int size; // the board’s size
private int piece size = 30; // the size of one playing piece, in pixels
private int panel width; // the panel’s width and height
private int panel height;

/** Constructor PuzzleWriter builds the graphics window.


* @param b - the slide puzzle that is displayed
* @param s - the size of the slide puzzle, e.g., 4 means 4 x 4 */
public PuzzleWriter(SlidePuzzleBoard b, int s)
{ board = b;
size = s;
panel width = piece size * size + 100;
panel height = piece size * size + 100;
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setTitle("Slide Puzzle");
my frame.setSize(panel width, panel height);
my frame.setVisible(true);
}

/** paintPiece draws piece p at position i,j in the window */


private void paintPiece(Graphics g, PuzzlePiece p, int i, int j)
{ int initial offset = piece size;
int x pos = initial offset + (piece size * j);
int y pos = initial offset + (piece size * i);
if ( p != null )
{ g.setColor(Color.white);
g.fillRect(x pos, y pos, piece size, piece size);
g.setColor(Color.black);
g.drawRect(x pos, y pos, piece size, piece size);
g.drawString(p.valueOf() + "", x pos + 10, y pos + 20);
}
else { g.setColor(Color.black);
g.fillRect(x pos, y pos, piece size, piece size);
}
}
...
446

Figure 8.16: output-view class for puzzle game (concl.)


/** paintComponent displays the puzzle in the frame. */
public void paintComponent(Graphics g)
{ g.setColor(Color.yellow);
g.fillRect(0, 0, panel width, panel height);
PuzzlePiece[][] r = board.contents();
for ( int i = 0; i != size; i= i+1 )
{ for ( int j = 0; j != size; j = j+1 )
{ paintPiece(g, r[i][j], i, j); }
}
}

/** displayPuzzle displays the current state of the slide puzzle. */


public void displayPuzzle()
{ this.repaint(); }

/** printError displays an error message.


* @param s - the error message */
public void printError(String s)
{ JOptionPane.showMessageDialog(null, "PuzzleWriter error: " + s ); }
}

k = k + 1;
}
}
return answer;
}
}

8.11 Testing Programs with Arrays


Programs with arrays prove notoriously difficult to test thoroughly, because we often
use an arithmetic expression as an array index, and the expression might compute to
an unacceptable integer. Here is an example: We have this method, which attempts
to exchange two adjacent array elements:
/** exchange swaps the values in elements r[i] and r[i-1] */
public void exchange(int[] r, int i)
{ int temp = r[i];
r[i] = r[i - 1];
r[i - 1] = temp;
}
8.11. TESTING PROGRAMS WITH ARRAYS 447

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);

shows that the attempted indexing, r[i], leads to an error.


The tests make clear that parameter i must be greater than zero and less than
the length of the array. We modify the method to read,
public void exchange(int[] r, int i)
{ if ( i > 0 && i < r.length )
{ int temp = r[i];
r[i] = r[i - 1];
r[i - 1] = temp;
}
else { ... announce there is a problem ... }
}

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

public void exchange(int[] r, int i)


{ if ( r != null && i > 0 && i < r.length )
{ int temp = r[i];
r[i] = r[i - 1];
r[i - 1] = temp;
}
else { ... announce there is a problem ... }
}

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 initialization statement:

int[] r = {1, 2, 4, 8, 16, 32};

• array-length attribute:

int[] r = new int[6];


r[0] = 1;
for ( int i = 1; i < r.length; i = i + 1 )
{ r[i] = r[i - 1] * 2; }

• two-dimensional array (from Figure 13):

private PuzzlePiece[][] board;


...
board = new PuzzlePiece[size][size];
..
board[size - 1 - row][size - 1 - col] = p;

• public static final variable (from Figure 9):

public static final int QUEEN = 12;

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.)

• one-dimensional array: an array whose collection is a “sequence” of values, that


is, each element is named by a single integer index, e.g., votes[2] names the
third integer value in the sequence of values held by votes.
450

• database: a large collection of data values that must be maintained by means


of insertions, retrievals, and deletions of the data values.

• key: the identity code used to retrieve a data value saved in a database.

• final variable: a variable whose value cannot be changed after it is initialized.

• two-dimensional arrays: “an array of arrays” that is typically drawn as a


matrix or grid. Each element is named by a pair of integer indexes, e.g.,
election[i][j]. The first index is the row index, and the second index is the
column index, where the terms refer to the depiction of the array as a matrix.

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).

• Arrays can be constructed with multiple dimensions—a one-dimensional array


is a sequence of elements; a two-dimensional array is a matrix—an array of
arrays. The rows of a two-dimensional array can have different lengths.

8.13 Programming Projects


1. Extend the simplistic vote-counting application in Figure 1 in the following
ways:

(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:

initialize the set of primes, P, to be all integers in 2..n;


for ( i = 2; 2*i <= n; i = i+1 )
{ remove from P all multiples of i };
print the contents of P;

Implement this algorithm by modelling P as an array of booleans: boolean[] P


= new boolean[n + 1]. (Hint: P[i] == false means that i is definitely not a
prime.)

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) Here is the new specification for class BarGraphWriter:

class BarGraphWriter helps a user draw bar graphs


Constructor
BarGraphWriter(int x pos, draw the x- and y-axes of the graph.
int y pos, int x length, int The pair, x pos, y pos, state the coor-
y height, String top label) dinates on the window where the two
axes begin. The x-axis extends from
x pos, y pos to the right for x length
pixels; the y-axis extends upwards from
x pos, y pos for y height pixels. The
label placed at the top of the y-axis is
top label. (The label placed at the bot-
tom of the y axis is always 0.)
Method
setBar(String label, int draws a new bar in the graph, to the
height, Color c) right of the bars already drawn, where
the label underneath the bar is label,
the height of the bar, in pixels, is
height, and the bar’s color is c.

(b) Here is the new specification for class PointGraphWriter:


452

class PointGraphWriter helps a user draw a graph of plotted


points
Constructor
PointGraphWriter(int x pos, draw the the vertical and horizontal
int y pos, int axis length, axes of the graph, such that the inter-
String x label, String section point of the axes lies at position
y label) x pos, y pos. Each axis has the length,
axis length pixels. The beginning la-
bels of both the x- and y-axes are 0; the
label at the top of the y-axis is y label,
and the label at the end of the x-axis is
x label.
Method
setPoint(int x increment, plot another point of the graph, so that
int y height) its x-position is x increment pixels to
the right of the last point plotted, and
its y-position is y height on the y-axis.
(c) Here is the revised specification for class PieChartWriter:

class PieChartWriter helps a user draw a pie chart


Method
setSlice(String label, int add a new “slice” to the chart, such that
amount, Color c) amount indicates the amount of the slice,
and c is the slice’s color. The label is
printed to the right of the pie, and it is
printed in the color, c.

4. Another form of coding words is by means of a transposition code, which encodes


a message by transposing its letters. The simplest form of transposition code
works as follows:

(a) A string, s, is read; say that s has length n.


(b) The smallest square matrix that can hold n characters is created.
(c) The characters in s are copied one by one into the columns of the matrix.
(d) The output is the rows of the matrix.
For example, the input string, abcdefghijklmn, would be stored into a 4-by-4
matrix as follows:

a e i m
b f j n
8.13. PROGRAMMING PROJECTS 453

c g k x
d h l x

The output would be the words, aeim, bfjn, cgkx, dhlx.


Write an application that implements this algorithm. Next, write an application
that decodes words produced by the algorithm.

5. To continue the development of the preceding Project, here is a slightly more


sophisticated transposition code, based on a numeric key:

(a) An integer “key” of m distinct digits is read, and a string, s of length n is


read.
(b) The smallest matrix with m columns that can hold n characters is created.
(c) The characters in s are copied one by one into the columns of the matrix.
(d) The digits in the key are sorted, and the columns of the array are accord-
ingly rearranged.
(e) The output is the rows of the matrix.

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

Since 421 “sorted” is 124, the columns are rearranged to appear:

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

Write an application that lets a computerized dealer and two computerized


players play this game. (Make the computerized player smart enough that it
surrenders a card that has count 6 or less.)
Next, modify the application so that one human player plays against one com-
puterized player.
Finally, modify the application so that two human players play against each
other; only the dealer is computerized.

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.

10. Revise the “21” card game as follows:

(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.

12. Here is a standard memory game, known as “Concentration” or “Husker Du”:


A matrix is filled with pairs of letters, and the letters are covered. Next, two
players take turns trying to discover the letter pairs: A player uncovers two
letters; if the letters match, the letters remain uncovered, the player scores one
456

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:

• an empty board position that is surrounded by exactly three cells gets a


cell placed on it. (The new cell “comes to life.”)
• a board position occupied by a cell retains the cell if the position was sur-
rounded by exactly 2 other cells. (Otherwise the cell disappears—“dies”—
due to “loneliness” or “overcrowding.”)

Here is an example of two seconds of the animation on a 5-by-5 board, where


an X denotes a cell and . denotes an empty space:

.X.XX ...XX ...XX


X...X ....X ....X
XXX.. => X.X.. => ...X.
..X.X X.X.. ..XX.
X...X ...X. .....

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.

(Hint: read the Supplement section on sorting.)


After you have implemented all three programs, perform case studies to deter-
mine when one strategy performs better than another. This problem is famous
because there is no efficient algorithm for best filling the bins.

8.14 Beyond the Basics


8.14.1 Sorting

8.14.2 Searching

8.14.3 Time-Complexity Measures

8.14.4 Divide-and-Conquer Algorithms

8.14.5 Formal Description of Arrays

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

for ( i = 0; i != r.length; i = i+1 )


{ Find the least element in r within the range
r[i] to r[r.length-1]; say that it is at r[j].
Exchange r[i] with r[j].
}

Here is the algorithm in action. Say that we have this array, r:


0 1 2 3 4
r 11 8 -2 7 10

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

Figure 15 shows the method.


There is more than one way to sort an array; a second classic approach, called
insertion sort, rearranges elements the way most people sort a hand of playing cards:
Start with the first card (element), then take the second card (element) and insert it
either before or after the first card, so that the two cards are in order; then take the
third card and insert it in its proper position so that the three cards are ordered, and
so on. Eventually, all the cards are inserted where they belong in the ordering.
The algorithm based on this idea is simply stated as:
for ( i=1; i < r.length; i = i+1 )
{ Insert r[i] in its proper place within the already sorted prefix,
r[0]..r[i-1].
}
460

Figure 8.17: selection sort


/** selectionSort sorts the elements of its array parameter
* @param r - the array to be sorted */
public void selectionSort(int[] r)
{ for ( int i = 0; i != r.length; i = i+1 )
// invariant: subarray r[0]..r[i-1] is sorted
{ int j = findLeast(r, i, r.length-1); // get index of least element
int temp = r[i];
r[i] = r[j];
r[j] = temp;
}
}

/** findLeast finds the index of the least element in r[start]..r[end]


* @param r - the array to be searched
* @param start - the starting element for the search
* @param end - the ending element for the search
* @return the index of the smallest element in r[start]..r[end] */
private int findLeast(int[] r, int start, int end)
{ int least = start;
for ( int i = start+1; i <= end; i = i+1 )
// invariant: least is index of least element in range r[start]..r[i-1]
{ if ( r[i] < r[least] ) { least = i; } }
return least; }

If we apply the algorithm to the example array, r, seen above,


0 1 2 3 4
r 11 8 -2 7 10

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 8.18: insertion sort


/** insertionSort sorts the elements of its array parameter
* @param r - the array to be sorted */
public static void insertionSort(int[] r)
{ for ( int i = 1; i < r.length; i = i+1 )
// invariant: prefix r[0]..r[i-1] is sorted
{ int v = r[i]; // v is the next element to insert into the prefix
int j = i;
while ( j != 0 && r[j-1] > v )
// invariants:
// (i) the original prefix, r[0]..r[i-1],
// is now arranged as r[0]..r[j-1], r[j+1]..r[i];
// (ii) all of r[j+1]..r[i] are greater than v
{ r[j] = r[j-1];
j = j-1;
}
r[j] = v;
}
}

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.

4. Why is the test expression, j != 0, required in the while-loop in method insertionSort?

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:

boolean did_exchanges = true;


while ( did_exchanges )
{ did_exchanges = false;
for ( int i = 1; i < r.length; i = i+1 )
{ If r[i] < r[i-1},
then exchange them and assign did_exchanges = true.
}
}

Program this sorting method.

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

Set searching = true.


Set the lower bound of the search to be 0 and the upper bound of the
search to be the last index of array, r.
while ( searching && lower bound <= upper bound )
{ index = (lower bound + upper bound) / 2;
if ( item == r[index] ) { found the item---set searching = false; }
else if ( item < r[index] ) { reset upper bound = index-1; }
else { reset lower bound = index+1; }
}

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

Figure 8.19: binary search


/** binarySearch searches for an item in a sorted array
* @param r - the array to be searched
* @param item - the desired item in array r
* @return the index where item resides in r; if item is not
* found, then return -1 */
public int binarySearch(int[] r, int item)
{ int lower = 0;
int upper = r.length - 1;
int index = -1;
boolean searching = true;
while ( searching && lower <= upper )
// (1) searching == true implies item is in range r[lower]..r[upper],
// if it exists in r at all.
// (2) searching == false implies that r[index] == item.
{ index = (lower + upper) / 2;
if ( r[index] == item )
{ searching = false; }
else if ( r[index] < item )
{ lower = index + 1; }
else { upper = index - 1; }
}
if ( searching )
{ index = -1; } // implies lower > upper, hence item not in r
return index;
}

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

boolean searching = true;


while ( searching && index != r.length )
{ if ( r[index] == item )
{ searching = false; }
else { index = index + 1; }
}
would examine four elements of the array to locate element 10. The binary search
examined just two. Binary search’s speedup for larger arrays is enormous and is
discussed in the next section.
Binary search is a well-known programming challenge because it is easy to for-
mulate incorrect versions. (Although the loop in Figure 17 is small, its invariant
suggests that a lot of thought is embedded within it.) Also, small adjustments lead
to fascinating variations. Here is a clever reformulation, due to N. Wirth:
public int binarySearch(int[] r, int item)
{ int lower = 0;
int upper = r.length-1;
int index = -1;
while ( lower <= upper )
// (1) lower != upper+2 implies that item is in range
// r[lower]..r[upper], if it exists in r at all
// (2) lower == upper+2 implies that r[index] == item
{ index = (lower + upper) / 2;
if ( item <= r[index] )
{ upper = index - 1; };
if ( item >= r[index] )
{ lower = index + 1; };
}
if ( lower != upper+2 )
{ index = -1; }
return index;
}
This algorithm merges variable searching in Figure 17 with the lower and upper
bounds of the search so that the loop’s test becomes simpler. This alters the loop
invariant so that the discovery of item is indicated by lower == upper+2.
Both searching algorithms must terminate, because the expression, upper-lower
decreases in value at each iteration, ensuring that the loop test will eventually go
false.

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

2. Here is a binary search method due to R. Howell:

public int search(int[] r, int item)


{ int answer = -1;
if ( r.length > 0 )
{ int lower = 0;
int upper = r.length;
while ( upper - lower > 1 )
// item is in r[lower]..r[upper-1], if it is in r
{ int index = (lower + upper) / 2;
if ( r[index] > item )
{ upper = index; }
else { lower = index; }
}
if ( r[lower]== item ) { answer = lower; }
}
return answer;
}

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.)

8.14.3 Time-Complexity Measures


The previous section stated that binary search computes its answer far faster than
does linear search. We can state how much faster by doing a form of counting analysis
on the respective algorithms. The analysis will introduce us to a standard method
for computing the time complexity of an algorithm. We then apply the method to
analyze the time complexity of selection sort and insertion sort.
To analyze a searching algorithm, one counts the number of elements the algorithm
must examine to find an item (or to report failure). Consider linear search: If array
r has, say, N elements, we know in the very worst case that a linear search must
examine all N elements to find the desired item or report failure. Of course, over
many randomly generated test cases, the number of elements examined will average
to about N/2, but in any case, the number of examinations is directly proportional
to the the array’ length, and we say that the algorithm has performance of order N
(also known as linear) time complexity.
For example, a linear search of an array of 256 elements will require at most 256
examinations and 128 examinations on the average.
Because it halves its range of search at each element examination, binary search
does significantly better than linear time complexity: For example, a worst case
binary search of a 256-element array makes one examination in the middle of the 256
466

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:

E(N ) = 1 + E(N/2), for N > 1


E(1) = 1

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:

E(2M ) = 1 + E(2M −1 ), for M > 0


E(20 ) = 1

After several calculations with this definition (and a proof by induction—see the
Exercises), we can convince ourselves that

E(2M ) = M + 1

a remarkably small answer!


We say that the binary search algorithm has order log N (or logarithmic) time
complexity. (Recall that log N, or more precisely, log2 N, is N’s base-2 logarithm, that
is, the exponent, M, such that 2M equals N. For example, log 256 is 8, and log 100
falls between 6 and 7.) Because we started our analysis with the assumption that N
= 2M , we conclude that
E(N ) = (log N ) + 1

which shows that binary search has logarithmic time complexity.


It takes only a little experimentation to see, for large values of N, that log N is
significantly less than N itself. This is reflected in the speed of execution of binary
search, which behaves significantly better than linear search for large-sized arrays.
8.14. BEYOND THE BASICS 467

Analysis of Sorting Algorithms


Of course, binary search assumes that the array it searches is sorted, so we should
calculate as well the time complexity of the sorting algorithms we studied. The two
factors in the performance of a sorting algorithm are (i) the number of comparisons
of array elements, and (ii) the number of exchanges of array elements. If either of
these measures is high, this slows the algorithm.
Consider selection sort first (Figure 15); it locates and exchanges the smallest
element, then the next smallest element, and so on. For an array of length N, it
uses N-1 comparisons to find the smallest element, N-2 comparisons to find the next
smallest element, and so on. The total number of comparisons is therefore
(N-1) + (N-2) + ...downto... + 2 + 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

This is the same sequence we encountered in our analysis of selection sort, so we


conclude that insertion sort also has quadratic time complexity.
Although selection sort’s time complexity is stable across all possible permutations
of arrays to be sorted, insertion sort executes much faster when it is given an almost
completely sorted array to sort. This is because insertion sort shifts elements only
when they are out of order. For example, if insertion sort is given an array of length
N+1 where only one element is out of order, it will take only order N (linear) time to
shift the element to its proper position. For this reason, insertion sort is preferred for
sorting almost-sorted arrays.
468

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.

3. Perform time-complexity analyses of the following methods:

(a) For Figure 1, Chapter 7, measure the time complexity of summation(N),


depending on the value of N. Count the number of assignments the method
makes.
(b) For Figure 3, Chapter 7, measure the time complexity of findChar(c,
s), depending on the lengths of string s. Count the number of charAt
operations the method makes.
(c) For Figure 13, measure the time complexity of paint, depending on the
size of array that must be painted. Count the number of invocations of
paintPiece.

4. Our time-complexity analyses are a bit simplistic: a precise time-complexity


analysis would count every operation that a computer’s processor makes, that
is, every arithmetic operation, every comparison operation, every variable ref-
erence, every assignment, every method invocation, every method return, etc.
Perform such a detailed analysis for the algorithms in the previous Exercise; for
8.14. BEYOND THE BASICS 469

linear search; for binary search. Are your answers significantly different than
before?

5. Use mathematical induction to prove that E(2M ) = M + 1, for all nonnegative


values of M. This requires that you prove these two claims:

• basis step: E(20 ) = 0 + 1


• induction step: Assume that E(2i ) = i + 1 holds true. Use this to prove
E(2i+1 ) = (i + 1) + 1.

6. Use mathematical induction to prove that (N-1) + (N-2) + ...downto... +


2 + 1 equals (1/2)(N 2 ) − (1/2)N , for all values of N that are 2 or larger. This
requires that you prove these two claims:

• basis step: (2-1) + (2-2) + ...downto... + 2 + 1 equals (1/2)(2 2 )−(1/2)2.


(Hint: read the sequence, 1 + ...downto... + 1 as being just the one-
element sequence, 1.)
• induction step: Assume that (i-1) + (i-2) + ...downto... + 2 + 1 equals
(1/2)(i2 )−(1/2)i. Use this to prove ((i+1)-1) + ((i+1)-2) + ...downto...
+ 2 + 1 equals (1/2)((i + 1)2 ) − (1/2)(i + 1).

8.14.4 Divide-and-Conquer Algorithms


In the previous section, we saw that the binary search algorithm has a significantly
better time complexity than the linear search algorithm. The time measurement for
binary search was expressed by a recursive definition, which suggests that a recursion
might be a factor in binary search’s performance. This is indeed the case—binary
search is an example of a style of recursion known as divide and conquer, which we
study in this section.
First, Figure 18 shows binary search written in recursive style. To search an entire
array, a, for a value, v, the method is invoked as binarySearch(a, v, 0, a.length-1).
The method clearly shows that, at each recursive invocation, the segment searched is
divided in half. Eventually, the desired item is found or the segment is divided into
nothing.
The method in the Figure is an example of a divide-and-conquer algorithm, so
called because the algorithm divides its argment, the array, into smaller segments at
each invocation. The divide-and-conquer pattern uses recursion correctly, because
each recursive invocation operates on parameters (the array segments) that grow
smaller until they reach a stopping value (size 0).
470

Figure 8.20: binary search by recursion


/** binarySearch searches for an item within a segment of a sorted array
* @param r - the array to be searched
* @param item - the desired item
* @param lower - the lower bound of the segment
* @param upper - the upper bound of the segment
* @return the index where item resides in r[lower]..r[upper];
* return -1, if item is not present in the segment of r */
public int binarySearch(int[] r, int item, int lower, int upper)
{ int answer = -1;
if ( lower <= upper )
{ int index = (lower + upper) / 2;
if ( r[index] == item )
{ answer = index; }
else if ( r[index] < item )
{ answer = binarySearch(r, item, index + 1, upper); }
else { answer = binarySearch(r, item, lower, index - 1); }
}
return answer;
}

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

Figure 8.21: merge sort


/** mergeSort builds a sorted array segment
* @param r - the array
* @param lower - the lower bound of the segment to be sorted
* @param upper - the upper bound of the segment to be sorted
* @return a sorted array whose elements are those in r[lower]..r[upper] */
public int[] mergeSort(int[] r, int lower, int upper)
{ int[] answer;
if ( lower > upper ) // is it an empty segment?
{ answer = new int[0]; }
else if ( lower == upper ) // is it a segment of just one element?
{ answer = new int[1];
answer[0] = r[lower];
}
else // it is a segment of length 2 or more, so divide and conquer:
{ int middle = (lower + upper) / 2;
int[] s1 = mergeSort(r, lower, middle);
int[] s2 = mergeSort(r, middle+1, upper);
answer = merge(s1, s2);
}
return answer;
}

/** 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:

C(2M ) = C(2M −1 ) + C(2M −1 ) + 2M


C(20 ) = 0

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

and since N = 2M , we have that

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]:

1. Rearrange (partition) r into two nonempty subarrays so that there is an index,


m, such that all the elements in r[lower]..r[m] are less than or equal to all
elements in r[m+1]..r[upper].

2. Sort the partition r[lower]..r[m].

3. Sort the partition r[m+1]..r[upper].

The end result must be the array entirely sorted.


Figure 20 gives the quickSort method, which is invoked as quickSort(r, 0,
r.length-1), for array r. The hard work is done by partition(r, lower, upper),
which partitions the elements in the range r[lower]..r[upper] into two groups. The
method uses the element at r[lower] as the “pivot” value for partitioning as it scans
the elements from left to right, moving those values less than the pivot to the left
side of the subarray. Once all the elements are scanned, the ones less than the pivot
form the first partition, and the ones greater-or-equal to the pivot form the second
partition.
474

Figure 8.22: quicksort


/** quickSort sorts an array within the indicated bounds
* @param r - the array to be sorted
* @param lower - the lower bound of the elements to be sorted
* @param upper - the upper bound of the elements to be sorted */
public void quickSort(int[] r, int lower, int upper)
{ if ( lower < upper )
{ int middle = partition(r, lower, upper);
quickSort(r, lower, middle);
quickSort(r, middle+1, upper);
}
}

/** partition rearranges an array’s elements into two nonempty partitions


* @param r - an array of length 2 or more
* @param lower - the lower bound of the elements to be partitioned
* @param upper - the upper bound of the elements to be partitioned
* @return the index, m, such that all elements in the nonempty partition,
* r[lower]..r[m], are <= all elements in the nonempty partition,
* r[m+1]..r[upper] */
private int partition(int[] r, int lower, int upper)
{ int v = r[lower]; // the ‘‘pivot’’ value used to make the partitions
int m = lower - 1; // marks the right end of the first partition
int i = lower + 1; // marks the right end of the second partition
while ( i <= upper )
// invariant: (i) all of r[lower]..r[m] are < v
// (ii) all of r[m+1]..r[i-1] are >= v,
// and the partition is nonempty
{ if ( r[i] < v )
{ // insert r[i] at the end of the first partition
// by exchanging it with r[m+1]:
m = m + 1;
int temp = r[i];
r[i] = r[m];
r[m] = temp;
}
i = i + 1;
}
if ( m == lower - 1 ) // after all the work, is the first partition empty?
{ m = m + 1; } // then place r[lower], which is v, into it
return m;
}
8.14. BEYOND THE BASICS 475

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

Since m is 2, the partitions are r[0]..r[2] and r[3]..r[6].


Once a partitioning step is complete, quickSort recursively sorts the two parti-
tions. This causes each subarray, r[0]..r[2] and r[3]..r[6], to be partitioned and
476

recursively sorted. (That is, the invocation, quicksort(r, 0, 2) invokes partition(r,


0, 2), and quicksort(r, 3, 6) invokes partition(r, 3, 6), and so on.) Eventually,
partitions of size 1 are reached, stopping the recursive invocations.
For quickSort to perform at its best, the partition method must generate par-
titions that are equally sized. In such a case, each recursive invocation of quickSort
operates on an array segment half the size of the previous one, and the time complex-
ity is the same as mergesort—order N log N. But there is no guarantee that partition
will always break an array into two equally sized partitions—if the pivot value, v, is
the largest (or smallest) value in an array segment of size N, then partition creates
one partition of size 1 and one of size N-1. For example, if array r was already sorted
0 1 2 3 4 5 6
r 1 3 4 5 7 8 9

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.

2. Write an execution trace of mergeSort applied to the array int[] r = {5, 8,


-2, 11, 9}.

3. Rewrite mergeSort in Figure 19 so that it does not create multiple new arrays.
Instead, use this variant:

/** mergeSort sorts a segment of an array, r


* @param r - the array whose elements must be sorted
* @param scratch - an extra array that is the same length as r
* @param lower - the lower bound of the segment to be sorted
* @param upper - the upper bound of the segment to be sorted */
public void mergeSort(int[] r, int[] scratch, int lower, int upper)
8.14. BEYOND THE BASICS 477

{ ...
mergeSort(r, scratch, lower, middle);
mergeSort(r, scratch, middle+1, upper);
...
}

The method is initially invoked as follows: mergeSort(a, new int[a.length],


0, a.length-1).

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.

6. Because quickSort’s partition method is sensitive to the pivot value it chooses


for partitioning, a standard improvement is to revise partition so that, when it
partitions a subarray of size 3 or larger, partition chooses 3 array elements from
the subarray and picks the median (the “middle value”) as the pivot. Revise
partition in this way.

8.14.5 Formal Description of Arrays


Arrays cause us to augment the syntax of data types, object construction, and vari-
ables to our Java subset. First, for every data type, T, T[] is the data type of
“T-arrays.” The precise syntax of data types now reads

TYPE ::= PRIMITIVE_TYPE | REFERENCE_TYPE


PRIMITIVE_TYPE ::= boolean | ... | int | ...
REFERENCE_TYPE ::= IDENTIFIER | TYPE[]

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:

DECLARATION ::= TYPE IDENTIFIER [[ = INITIAL_EXPRESSION ]]? ;

INITIAL_EXPRESSION ::= EXPRESSION


| { [[ INITIAL_EXPRESSION_LIST ]]? }
INITIAL_EXPRESSION_LIST ::= INITIAL_EXPRESSION [[ , INITIAL_EXPRESSION ]]*
478

(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:

double[][] d = { {0.1, 0.2}, {}, {2.3, 2.4, 2.6}};

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:

EXPRESSION ::= ... | STATEMENT_EXPRESSION


STATEMENT_EXPRESSION ::= OBJECT_CONSTRUCTION | ...

OBJECT_CONSTRUCTION ::= ... | new ARRAY_ELEMENT_TYPE DIMENSIONS

ARRAY_ELEMENT_TYPE ::= PRIMITIVE_TYPE | IDENTIFIER


DIMENSIONS ::= [ EXPRESSION ] [[ [ EXPRESSION ] ]]* [[ [] ]]*

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]:

1. EXPRESSION is computed to an integer value, v. (If v is negative, an exception


results.)

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.

4. The object’s address, a, is returned as the result.

When an array variable is initialized with an array object, as in int[] r = new


int[3], data-type checking and execution semantics proceed the same as with any
other variable initialization: The data type of the right-hand-side expression must
be a subtype of the left-hand-side type, and the address of the constructed object is
assigned to the left-hand-side variable’s cell.

References and Assignments


Elements of arrays are referenced with bracket notation, e.g., r[i + 1] = r[0]. Here
is the syntax for expressions and assignments extended to arrays:
ASSIGNMENT := VARIABLE = EXPRESSION
VARIABLE ::= IDENTIFIER | ... | RECEIVER [ EXPRESSION ]
EXPRESSION ::= ... | VARIABLE

RECEIVER ::= IDENTIFIER | ... | RECEIVER [ EXPRESSION ]

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:

1. RECEIVER is computed to its result, which will be an address, a, of an array


object.

2. EXPRESSION is computed to its result, which must be an integer, v.

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

the assignment, r[0][2] = r[0][s[1] + 1], would execute these steps:

1. The variable part, r[0][2], computes to an address:

(a) The leftmost r computes to a1.


(b) r[0] computes to the address, a1[0], but this phrase is used as a receiver
(of the message, [2]), so a1[0] is dereferenced, producing a2.
(c) The address, a2[2], is formed as the address of the left-hand side variable.
This is the target of the assignment.

2. The right-hand side, r[0][s[1] + 1], computes to an integer:

(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.

3. 7 is assigned to address a2[2].


The above description of array assignment omits an important subtlety that is
specific to the Java language: When this assignment is executed,
RECEIVER [ EXPRESSION1 ] = EXPRESSSION2

The complete listing of execution steps goes as follows:


8.14. BEYOND THE BASICS 481

1. RECEIVER is computed to its value, which will be an address, a, of an array


object. The run-time type information is extracted from a; say that it is
element type[size].

2. EXPRESSION1 is computed to an integer, v. If v is nonnegative and less than


size, then the address, a[v], is formed as the target of the assignment.

3. EXPRESSSION2 is computed to its result, w.

4. This is the surprising, additional step: If w is not a primitive value, then it is an


address of an object—the run-time type, t, is fetched from the object at address
w and is compared to element type to verify that t is a subtype of element type.

5. If the types are compatible, w is assigned to the cell at a[v]; otherwise, an


exception is thrown.

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:

public void assignPanel(JPanel[] r, JPanel f)


{ r[0] = f; }

The Java compiler examines the method and judges it acceptable. Next, we write
this class:

public class MyPanel extends JPanel


{ public MyPanel() { }

public void paintComponent(Graphics g) { }

public void newMethod() { }


}

This class is also acceptable to the Java compiler. But now, we play a trick:

MyPanel[] panels = new MyPanels[2];


JPanel x = new JPanel();
assignPanel(panels, x);
panels[0].newMethod();

Because MyPanel is a subtype of JPanel, panels is an acceptable actual parameter to


assignPanel, which apparently assigns a JPanel object into an array that is meant
to hold only MyPanel objects. If the assignment is allowed to proceed, then disaster
482

strikes at panels[0].newMethod(), which sends a message to an object that has no


newMethod.
This is the reason why every assignment to an array element must be type checked
at execution even though it was type checked previously by the Java compiler.
Chapter 9

Programming to Interfaces
9.1 Why We Need Specifications

9.2 Java Interfaces

9.2.1 Case Study: Databases

9.3 Inheritance

9.4 Reference Types, Subtypes, and instanceof

9.5 Abstract Classes

9.5.1 Case Study: Card Players

9.5.2 Class Hierarchies

9.5.3 Frameworks and Abstract Classes

9.6 Subtypes versus Subclasses

9.7 class Object and Wrappers

9.8 Packages

9.8.1 Generating Package APIs with javadoc

9.9 Case Study: An Adventure Game

9.9.1 Interfaces and Inheritance Together

9.9.2 Inheritance of Interfaces

9.10 Summary

9.11 Programming Projects

9.12 Beyond the Basics

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.

• Java packages, which group a collection of classes under one name.

This chapter’s title comes from a slogan that practicing programmers follow when
they build an application:

Program to the interface, not the implementation!

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.

9.1 Why We Need Specifications


A program is assembled from a collection of classes that must “work together” or
“fit together.” What does it mean for two classes to fit together? For some insight,
consider the following situation.
Say that you receive a portable disc player as a gift. When you try to operate
the player, nothing happens — the player requires batteries. What batteries fit into
the player? Fortunately, on the back of the player is the specification, “This player
requires two AA batteries.” With this information, you can obtain the correctly sized
components (the batteries) and fit them into the player. The completed “assembly”
operates.
The specification of the disc player’s batteries served several useful purposes:

• 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;

2. the extends construction, which lets us code a class by adding methods to a


class that already exists;

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.

9.2 Java Interfaces


In the previous chapters, we used informal specifications to design classes and to con-
nect them to their collaborator classes. But the Java language provides a construct,
called an interface, that lets us include a specification as an actual Java component
of an application—we type the interface into a file and compile it, just like a class.
Then, we use the compiled interface in two ways:

• 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

Figure 9.1: Java interface


/** BankAccountSpecification specifies the behavior of a bank account. */
public interface BankAccountSpecification
{ /** deposit adds money to the account
* @param amount - the amount of the deposit, a nonnegative integer */
public void deposit(int amount);

/** withdraw deducts money from the account, if possible


* @param amount - the amount of the withdrawal, a nonnegative integer
* @return true, if the the withdrawal was successful;
* return false, otherwise. */
public boolean withdraw(int amount);
}

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 }

where METHOD HEADER LINES is a sequence of header lines of methods, terminated by


semicolons. As a matter of policy, we insert a comment with each header line that
describes the intended behavior of the method.
The BankAccountSpecification interface is placed in its own file, BankAccountSpecification.java,
and is compiled like any other component. Once the interface is compiled, other
classes can use it in their codings, as we now see.
Your friend starts work on the mortgage-payment class. Although your friend does
not have the coding of the bank-account class, it does not matter — whatever the cod-
ing will be, it will possess the methods listed in interface BankAccountSpecification.
Therefore, your friend writes the class in Figure 2, which uses the BankAccountSpecification
as the data type for the yet-to-be-written bank-account class. The interface name,
BankAccountSpecification, is used as a data type, just like class names are used as
9.2. JAVA INTERFACES 487

Figure 9.2: class that references a Java interface


/** MortgagePaymentCalculator makes mortgage payments */
public class MortgagePaymentCalculator
{ private BankAccountSpecification bank account; // holds the address of
// an object that implements the BankAccountSpecification

/** Constructor MortgagePaymentCalculator initializes the calculator.


* @param account - the address of the bank account from which we
* make deposits and withdrawals */
public MortgagePaymentCalculator(BankAccountSpecification account)
{ bank account = account; }

/** makeMortgagePayment makes a mortgage payment from the bank account.


* @param amount - the amount of the mortgage payment */
public void makeMortgagePayment(int amount)
{ boolean ok = bank account.withdraw(amount);
if ( ok )
{ System.out.println("Payment made: " + amount); }
else { ... error ... }
}

...
}

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

Figure 9.3: a class that implements a Java interface


/** BankAccount manages a single bank account; as stated in its
* header line, it implements the BankAccountSpecification: */
public class BankAccount implements BankAccountSpecification
{ private int balance; // the account’s balance

/** Constructor BankAccount initializes the account */


public BankAccount()
{ balance = 0; }

// notice that methods deposit and withdraw match the same-named


// methods in interface BankAccountSpecification:

public void deposit(int amount)


{ balance = balance + amount; }

public boolean withdraw(int amount)


{ boolean result = false;
if ( amount <= balance )
{ balance = balance - amount;
result = true;
}
return result;
}

/** getBalance reports the current account balance


* @return the balance */
public int getBalance()
{ return balance; }
}

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:

BankAccount my_account = new BankAccount();


MortgageCalculator calc = new MortgageCalculator(my_account);
...
calc.makeMortgagePayment(500);
9.2. JAVA INTERFACES 489

Figure 9.4: class diagram of Java interface

MortgageCalculator BankAccount
makeMortagagePayment() deposit(int amount)
withdraw(int amount): boolean
balanceOf(): int
BankAccountSpecification
deposit(int amount)
withdraw(int amount): boolean

Since the Java compiler verified that BankAccount implements BankAccountSpecification,


the my account object can be an argument to the constructor method of MortgageCalculator.

A major advantage of using Java interfaces is this:


Class MortgageCalculator is not rewritten or changed to refer to BankAccount —
once class MortgageCalculator is correctly compiled with
interface BankAccountSpecification, it is ready for use.
In this way, components can be separately designed, written, compiled, and later
connected together into an application, just like the disc player and batteries were
separately manufactured and later connected together.
Figure 4 shows the class diagram for the example in Figures 1 to 3. To dis-
tinguish it from the classes, the Java interface is written in italics. Since class
MortageCalculator depends on (references) the interface, a dotted arrow is drawn
from it to the interface. Since class BankAccount implements the interface, a dotted
arrow with a large arrowhead is drawn from it to the interface.
The class diagram in Figure 4 shows that MortgageCalculator and BankAccount
do not couple-to/depend-on each other (in the sense of Chapter 6)—they both depend
on interface BankAccountSpecification, which is the “connection point” for the two
classes. Indeed, MortgageCalculator and BankAccount are simple examples of “sub-
assemblies” that connect together through the BankAccountSpecification interface.
This makes it easy to remove BankAccount from the picture and readily replace it by
some other class that also implements BankAccountSpecification. (See the Exercises
that follow this section.)
It is exactly this use of Java interfaces that allows teams of programmers build
large applications: Say that a team of programmers must design and build a com-
plex application, which will require dozens of classes. Perhaps the programmers are
divided into three groups, each group agrees to write one-third of the application,
and the three subassemblies will be connected together. If all the programmers first
agree on the interfaces where the three subassemblies connect, then each group can
independently develop their own subassembly so that it properly fits into the final
product
490

Exercises
1. Given this interface,

public interface Convertable


{ public double convert(int i); }

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; }
}

2. Given this interface,

public interface Convertable


{ public double convert(int i); }

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 void printConversion(int x)


{ System.out.println(convertor.convert(x)); }
}
9.2. JAVA INTERFACES 491

(b) public class Compute2 uses Convertable


{ public Compute2() { }

public void printIt(double x)


{ System.out.println(Convertable.convert(x)); }
}
(c) public class Compute3
{ Convertable c;

public Compute3()
{ c = new Convertable(); }

public void printIt(int v)


{ System.out.println(c.compute(v)); }
}

3. After you have answered the previous two questions, do the following:

• Place interface Convertable in the file, Convertable.java, and compile


it. Next, copy class C1 to the file, C1.java, in the same folder as Convertable.java,
and compile it.
• Place class Compute1 in the file, Compute1.java, in the same folder as
Convertable.java, and compile it.
• Now, place the following code in the file, Start.java, in the same folder
as Convertable.java, and compile it:
public class Start
{ public static void main(String[] args)
{ C1 c = new C1(1);
Compute1 computer = new Computer(c);
computer.printConversion(3);
}
}

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.

4. Reconsider interface BankAccountSpecification from Figure 1. We will use it


to improve the bank-accounts manager program from Section 8 of Chapter 6.
492

(a) Revise class BankAccount from Figure 11 of Chapter 6 so that it imple-


ments BankAccountSpecification. Next, revise class BankWriter (Figure
15, Chapter 6) and class AccountController (Figure 16, Chapter 6) so
that they invoke the methods of an object of type BankAccountSpecification
(and not BankAccount). Why does AccountController compile correctly
but BankWriter does not?
Repair interface BankAccountSpecification so that class BankWriter
compiles without errors. Does class AccountManager of Figure 16 require
any changes?
(b) Next, discard class BankAccount from Figure 11 of Chapter 6 and replace
it by this class:
public class SillyAccount implements BankAccountSpecification
{ public SillyAccount() { }
public void deposit(int amount) { }
public boolean withdraw(int amount) { return true; }
public int getBalance() { return 0; }
}
Change class AccountManager in Figure 16 so that it declares,
BankAccountSpecification account = new SillyAccount();
Does AccountManager compile without error? Do any of the other classes
require changes to compile?
This exercise demonstrates that Java interfaces make it simple to replace
one class in an assembly without rewriting the other classes.

5. Review the moving-ball animation in Chapter 7, Section 9.

(a) First, write a Java interface that describes MovingObjectBehavior as de-


fined in Table 10 of Chapter 7. Then make class MovingBall in Fig-
ure 12, Chapter 7, implement MovingObjectBehavior, and change classes
BounceController and BallWriter so that they mention only MovingObjectBehavior
(and not MovingBall).
(b) Based on the changes you made, redraw the class diagram in Figure 9,
Chapter 7.
(c) Next, replace class MovingBall by this class:
/** ThrobbingBall models a stationary ball that changes size */
public class ThrobbingBall implements MovingObjectBehavior
{ private int max_radius = 60;
private int increment = 10;
private int current_radius; // invariant: 0 <= current_radius < max_radius
private int position = 100;
9.2. JAVA INTERFACES 493

public ThrobbingBall() { current_radius = 0; }


public int xPosition() { return position; }
public int yPosition() { return position; }
public int radiusOf() { return current_radius; }
public void move()
{ current_radius = (current_radius + increment) % max_radius; }
}
and in Figure 16, Chapter 7, replace the MovingBall object constructed
within class BounceTheBall by
ThrobbingBall ball = new ThrobbingBall();
Recompile class BounceTheBall and execute the animation.

9.2.1 Case Study: Databases


In Chapter 8, Section 6, we designed a database, named class Database, to hold
a collection of “record” objects, each of which possessed a unique “key” object to
identify it. The database was designed to be general purpose, in the sense that
records might be library-book objects or bank-account objects, or tax records. As
stated in Chapter 8, the crucial behaviors were
1. The Database holds a collection of Record objects, where each Record holds a Key
object. The remaining structure of the Records is unimportant and unknown
to the database.

2. The Database will possess insert, find, and delete methods.

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

Figure 9.5: interfaces for Record and Key


/** Record is a data item that can be stored in a database */
public interface Record
{ /** getKey returns the key that uniquely identifies the record
* @return the key */
public Key getKey();
}

/** Key is an identification, or ‘‘key,’’ value */


public interface Key
{ /** equals compares itself to another key, m, for equality
* @param m - the other key
* @return true, if this key and m have the same key value;
* return false, otherwise */
public boolean equals(Key m);
}

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

Figure 9.6: implementing the database interfaces


/** BankAccount models a bank account with an identification key */
public class BankAccount implements Record
{ private int balance; // the account’s balance
private Key id; // the identification key

/** Constructor BankAccount initializes the account


* @param initial amount - the starting account balance, a nonnegative.
* @param id - the account’s identification key */
public BankAccount(int initial amount, Key id)
{ balance = initial amount;
key = id;
}

/** deposit adds money to the account.


* @param amount - the amount of money to be added, a nonnegative int */
public void deposit(int amount)
{ balance = balance + amount; }

/** getBalance reports the current account balance


* @return the balance */
public int getBalance() { return balance; }

/** getKey returns the account’s key


* @return the key */
public int getKey() { return key; }
}

/** IntegerKey models an integer key */


public class IntegerKey implements Key
{ private int k; // the integer key

/** Constructor IntegerKey constructs the key


* @param i - the integer that uniquely defines the key */
public IntegerKey(int i) { k = i; }

/** equals compares this Key to another for equality


* @param c - the other key
* @return true, if this key equals k’s; return false, otherwise */
public boolean equals(Key c)
{ return ( k == ((IntegerKey)c).getInt() ); }

/** getInt returns the integer value held within this key */
public int getInt() { return k; }
}
496

Figure 9.7: class diagram with interfaces

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

Database db = new Database(4); // see Figure 4, Chapter 8

BankAccount a = new BankAccount(500, new IntegerKey(1234));


boolean result1 = db.insert(a);

IntegerKey k = new IntegerKey(567);


BankAccount b = new BankAccount(1000, k);
boolean result2 = db.insert(b);

Record r = db.find(k); // retrieve object indexed by Key k


System.out.println(((BankAccount)r).getBalance()); // why is the cast needed?

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
{ ...

public void paintComponent(Graphics g)


{ ... instructions for painting on a panel ... }
}

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

• writing a method, paintComponent, that contains instructions for painting on


the panel.
When we construct an object from our class, say,
MyPanel p = new MyPanel(...);

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; }

public String getName()


{ return name; }

... // Other clever methods are here.


}
498

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:

public class PersonAddress extends Person


{ private String address;

public PersonAddress(String the_name, String the_addr)


{ super(the_name); // this gives the_name to Person’s constructor
address = the_addr;
}

public String getAddress()


{ return address; }

...
}

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,

PersonAddress x = new PersonAddress("fred", "new york");

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.,

System.out.println("Name: " + x.getName());


System.out.println("Address: " + x.getAddress());

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:

FIGURE HERE: PersonAddress ---|> Person


9.3. INHERITANCE 499

Inheritance is fundamentally different from Java interfaces, because inheritance


builds upon classes that are already written, whereas interfaces specify classes that
are not yet written (or unavailable). Inheritance is often used when someone has
written a basic class that contains clever methods, and other people wish to use the
clever methods in their own classes without copying the code — they write subclasses.
A good example is our extension of class JPanel, at the beginning of this section.
Here is another situation where inheritance can be useful: Again, say that class
Person has proved popular, and someone has written a useful class that writes infor-
mation about persons:

public class WritePerson


{ ...

public void writeName(Person p)


{ ... clever instructions to write p’s name ... }
}

If we write an application that manages persons plus their addresses, we can


exploit both class Person and class WritePerson by writing class PersonAddress
extends Person as shown above. Then, we can do this:

PersonAddress x = new PersonAddress("fred", "new york");


WritePerson writer = new WritePerson(...);
writer.writeName(x);
System.out.println("Address: " + x.getAddress());

The statement, writer.writeName(x) is crucial, because the writeName method ex-


pects an argument that has data type Person. Because x has data type PersonAddress
and because PersonAddress extends Person, x is acceptable to writeName. This be-
havior is based on subtyping and is explored in the next section.

Exercises
1. Given these classes,

public class Person


{ private String name;

public Person(String n) { name = n; }

public String getName() { return name; }

public boolean sameName(Person other)


{ return getName().equals(other.getName(); }
}
500

public class PersonAddress extends Person


{ private String address;

public PersonAddress(String the_name, String the_addr)


{ super(the_name);
address = the_addr;
}

public String getAddress() { return address; }

public boolean same(PersonAddress other)


{ return sameName(other) && address.equals(other.getAddress()); }
}

and these declarations:

Person p = new Person("fred");


Person q = new PersonAddress("ethel", "new york");

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.

9.4 Reference Types, Subtypes, and instanceof

When we first encountered data types in Chapter 3, we treated them as “species”


of values—int, double, and boolean were examples of such species. The classifying
values into species prevents inappropriate combinations of values, such as true && 3
— the Java compiler does data-type checking to spot such errors. Data-type checking
also spots bad actual-formal parameter combinations, such as Math.sqrt(true).
Numerical, boolean, and character values are primitive (non-object), and their
data types are called primitive types. The numeric primitive types are related by
subtyping: int is a subtype of double, written int <= double, because an integer can
be used in any situation where a double is required. For example, the integer 2 can
be used within
9.4. REFERENCE TYPES, SUBTYPES, AND INSTANCEOF 501

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; }

it is acceptable to send the method an actual parameter that is an integer, e.g.,


inverseOf(3). The Java compiler uses the subtyping relationship, int <= double, to
check the well formedness of these examples.
Subtyping relationships simplify our programming; in particular, cumbersome cast
expressions are not required. For example, it is technically correct but ugly to write
double d = 4.5 / ((double)2), and thanks to subtyping, we can omit the cast.
Begin footnote: Here is a listing of the subtyping relationships between the numeric
types:
byte <= int <= long <= float <= double

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
{ ... }

public class FrameTest3


{ public static void main(String[] args)
502

{ JFrame my_frame = new JFrame();


// insert a new panel into the frame:
my_frame.getContentPane().add(new MyPanel());
...
}
}
The add method invoked within main expects a JPanel object as its argument. But
since MyPanel <= JPanel, the newly constructed MyPanel object is acceptable.
This same phenomenon appeared at the end of the previous section when we used
a PersonAddress object as an argument to the writeName method of PersonWriter.
Similar subtyping principles also apply to Java interfaces: The Java compiler
uses interface names as data type names, and the compiler enforces a subtyping
relationship when an interface is implemented: if class C implements I, then C <=
I. This proves crucial when connecting together classes:
Reconsider the database example in Figures 5-7; we might write
Database db = new Database(4);
IntegerKey k = new IntegerKey(1234);
BankAccount b = new BankAccount(500, k);
boolean success = db.insert(b);
The database method, insert, expects an arguments with data type Record, but
it operates properly with one of type BankAccount, because BankAccount <= Record.
This subtyping can be justified by noting that a BankAccount has all the methods
expected of a Record, so insert executes as expected.
If we peek inside computer storage, we see that the above statements constructed
these objects:
a1 : Database
Database db == a1
Record[] base == a2
IntegerKey k == a3
int count == 1
BankAccount b == a4 public boolean insert(Record r) {...}
public Record find(Key k) {...}
public boolean delete(Key k) {...}
boolean success == true private int locationOf(Key k) {...}

a2 : Record[4]
0 1 2 3
a4 null null null a4 : BankAccount

int balance == 500


a3 : IntegerKey Key id == a3
...
int id == 1234
...
9.4. REFERENCE TYPES, SUBTYPES, AND INSTANCEOF 503

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; }

public String getString()


{ return s; }

public boolean equals(Key m) { ... }


}

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:

IntegerKey k1 = new IntegerKey(2);


StringKey k2 = new StringKey("two");
boolean answer = k1.equals(k2);

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:

Exception in thread "main" java.lang.ClassCastException: StringKey


at IntegerKey.equals(...)

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,

public class Person


{ private String name;

public Person(String n) { name = n; }

public String getName() { return name; }

public boolean sameName(Person other)


{ return getName().equals(other.getName(); }
}

public class PersonAddress extends Person


{ private String address;

public PersonAddress(String the_name, String the_addr)


{ super(the_name);
address = the_addr;
}

public String getAddress() { return address; }

public boolean same(PersonAddress other)


{ return sameName(other) && address.equals(other.getAddress()); }
}

and these declarations:

Person p = new Person("fred");


Person q = new PersonAddress("ethel", "new york");

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

(c) PersonAddress x = p; System.out.println(x.getAddress());


(d) Person x = q; System.out.println(x.getAddress());
(e) System.out.println(q.same(p));
2. Explain why this example fails to compile:

public class C
{ private int x;
public C() { x = 0; }
}

public class D extends C


{ public D() { super(); }
public void increment() { x = x + 1; }
}

If you are interested in repairing the example, read the section, “Subclasses and
Method Overriding,” at the end of the Chapter.

3. Use the instanceof operation to improve the existing implementations of interface


Key:

(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

BankAccount b = new BankAccount(500, new IntegerKey(1234));


IntegerKey k = new StringKey("lucy");
BankAccount lucy = new BankAccount(1000, k);
boolean result1 = db.insert(b);
boolean result2 = db.insert(lucy);

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"); }

What appears on the display?

4. Given these interfaces and classes,

public interface I
{ public int f(int i); }

public class C implements I


{ public C() { }
public int f(int i) { return i + 1; }
public void g() { }
}

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;

Which of the following expressions evaluate to true? x instanceof I; x


instanceof C; x instanceof D; y instanceof I; y instanceof C; y instanceof
D; z instanceof I; z instanceof C.
(c) Add casts, where necessary, so that the following statement sequence passes
the scrutiny of the Java compiler:
I x = new C();
x.f(21);
x.g();
C y = x;
y.g();
if ( x instanceof C )
{ x.g(); }

(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("!");

9.5 Abstract Classes


Simply stated, an abstract class is a class with missing method bodies. The missing
bodies are supplied by subclasses that extend the abstract class. Why would we use
such a construction? Here is a small example:
In the previous section, we pretended that a class Person was an important
component of many programs. Perhaps the author of class Person intended that
Person objects must have addresses, but the author did not wish to code the address
part. (The address might be a string or an integer or a color or ....) An abstract class
can state exactly the author’s wishes:
public abstract class Person // note the keyword, abstract
{ private String name;

public Person(String n)
{ name = n; }

public String getName()


{ return name; }

public abstract String getAddress(); // method will be written later

...
}

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

public PersonAddress(String the_name, String the_addr)


{ super(the_name); // this gives the_name to Person’s constructor
address = the_addr;
}

public String getAddress()


{ return address; }

...
}

We can construct new PersonAddress(...) objects, as before. And, we can construct


other variants, e.g.,

public class PersonWithInt extends Person


{ private int address;

public PersonWithInt(String the_name, int the_addr)


{ super(the_name);
address = the_addr;
}

public String getAddress()


{ return "" + address; } // make the int into a string

...
}

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.

9.5.1 Case Study: Card Players


An abstract class is “half interface” and “half superclass,” and it is most useful for
grouping, under a single data type name, classes that share methods.
Here is an example that illustrates good use of interfaces with abstract classes: In
Chapter 8, we saw how to design cards and card decks for a card game. (See Tables
7 and 8 and Figures 9 and 10 from that Chapter.) If we continue developing the card
game, we will design a class Dealer, which collaborates with cards and card decks
as well as with objects that are card players. At this point, we do not know exactly
how the card players will behave (this is dependent on the rules of the card game),
but class Dealer would expect that a card player has a method to receive a card
and has a method that replies whether a player wants to receive additional cards.
The obvious step is to write an interface; Figure 8 shows it. Now we can write a
510

Figure 9.8: interface for card playing


/** CardPlayerBehavior defines expected behaviors of card players */
public interface CardPlayerBehavior
{ /** wantsACard replies whether the player wants one more new card
* @return whether a card is wanted */
public boolean wantsACard();

/** receiveCard accepts a card and adds it to the player’s hand


* @param c - the card */
public void receiveCard(Card c);
}

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:

ComputerPlayer p = new ComputerPlayer(3);


9.5. ABSTRACT CLASSES 511

Figure 9.9: abstract class for card players


/** CardPlayer models an abstract form of card player */
public abstract class CardPlayer implements CardPlayerBehavior
{ private Card[] my hand; // the player’s cards
private int card count; // how many cards are held in the hand

/** CardPlayer builds the player


* @param max cards - the maximum cards the player can hold. */
public CardPlayer(int max cards)
{ my hand = new Card[max cards];
card count = 0;
}

/** 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

public void receiveCard(Card c)


{ my hand[card count] = c;
card count = card count + 1;
}

/** showCards displays the player’s hand


* @return an array holding the cards in the hand */
public Card[] showCards()
{ Card[] answer = new Card[card count];
for ( int i = 0; i != card count; i = i + 1 )
{ answer[i] = my hand[i]; }
return answer;
}
}
512

Figure 9.10: subclasses of CardPlayer


/** HumanPlayer models a human who plays cards */
public class HumanPlayer extends CardPlayer
{ /** HumanPlayer builds the player
* @param max cards - the maximum cards the player can hold */
public HumanPlayer(int max cards)
{ super(max cards); } // invoke constructor in superclass

public boolean wantsACard()


{ String response = JOptionPane.showInputDialog
("Do you want another card (Y or N)?");
return response.equals("Y");
}
}

/** ComputerPlayer models a computerized card player */


public class ComputerPlayer extends CardPlayer
{ /** ComputerPlayer builds the player
* @param max cards - the maximum cards the player can hold. */
public ComputerPlayer(int max cards)
{ super(max cards); } // invoke constructor in superclass

public boolean wantsACard()


{ boolean decision;
Card[] what i have = showCards();
... statements go here that examine what i have and
calculate a decision ...
return decision;
}
}
9.5. ABSTRACT CLASSES 513

HumanPlayer h = new HumanPlayer(3);


CardPlayer someone = p;
CardPlayerBehavior another = someone;

The following situation appears in computer storage:


a1 : ComputerPlayer
ComputerPlayer p == a1 public boolean wantsACard() { ... }
// from CardPlayer:
HumanPlayer h == a3 Card[] my hand ==
a2

CardPlayer someone == a1 int card count == 0


public void receiveCard(Card c) { ... }
public Card[] showCards() { ... }
CardPlayerBehavior another == a1

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

Figure 9.11: architecture of dealer and card players

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 <=

Although the assignment, CardPlayer someone = p, was legal, this initialization


is not—CardPlayer someone = new CardPlayer(3)—because objects cannot be con-
structed from abstract classes.
Figure 11 shows the architecture we have assembled with the example. The dia-
gram tells us that the Dealer collaborates through the CardPlayerBehavior interface,
which is implemented by the abstract class, CardPlayer. (The abstract parts are
stated in italics.)

Exercises
1. Write class Dealer based on this specification:

class Dealer models a dealer of cards


Responsibilities (methods)
dealTo(CardPlayerBehavior p) gives cards, one by one, to player p, until p no
longer wants a card
Collaborators: CardPlayerBehavior, CardDeck, Card

2. In the above specification, replace all occurrences of CardPlayerBehavior by


CardPlayer. Rewrite class Dealer accordingly. Compare the advantages and
disadvantages of the two variants of class Dealer.

3. One abstract class can extend another; here is an example:

/** Point models a geometric point */


public class Point
{ private int x;
9.5. ABSTRACT CLASSES 515

private int y;

public Point(int a, int b)


{ x = a;
y = b;
}

public int xPosition() { return x; }


public int yPosition() { return y; }
}

/** Shape models a two-dimensional geometric shape */


public abstract class Shape
{ private Point upper_left_corner;

public Shape(Point location)


{ upper_left_corner = location; }

/** 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();
}

/** Polygon models a polygon shape */


public abstract class Polygon extends Shape
{ private Point[] end_points; // the nodes (corners) of the polygon

public Polygon(Point upper_left_corner)


{ super(upper_left_corner); }

/** setCorners remembers the nodes (corners) of the polygon */


public void setCorners(Point[] corners)
{ end_points = corners; }
}

Now, write a concrete class, Rectangle, that extends Polygon. The constructor
method for class Rectangle can look like this:

public Rectangle(Point location, int width, int height)


516

Figure 9.12: hierarchy of animals

Animal

WarmBlooded (mammal)
Feline

Lion
Tiger
. . .
Equine
Horse
Zebra
Bovine
. . .
. . .
ColdBlooded (reptile)
. . .

Next, write a concrete class, Circle, that extends Shape.

9.5.2 Class Hierarchies


When we write programs that manipulate many different types of objects, we will
find it helpful to draw the classes as a “hierarchy” or “taxonomy” based on their
subclass relationships.
This approach comes from real-life taxonomies, like the one in Figure 12, which
simplistically models the zoology of part of the animal kingdom.
The characteristics of animals appear at the internal positions of the hierarchy,
and actual animals appear at the end positions—the “leaves” of the “tree.” (Animal
is the “root” of the “tree.”) When one characteristic is listed beneath another, it
means that the first is more specific or a “customization” of the second, in the sense
that the first has all the behaviors and characteristics of the second. For example,
being Equine means having all the characteristics of a WarmBlooded entity.
The zoological taxonomy helps us make sense of the variety of animals and reduces
the amount of analysis we apply to the animal kingdom. In a similar way, in a
taxonomy of classes, when a class, A is a superclass of class B, we place B underneath
A in the hierarchy.
For example, if we designed a computer program that worked with the animal-
kingdom taxonomy, the program would create objects for the classes at the leaves,
for example,
Horse black_beauty = new Horse(...);

and it might assign,


9.5. ABSTRACT CLASSES 517

Equine a_good_specimen = black_beauty;

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.,

public abstract class Animal


{
// fields and methods that describe an animal
}

public abstract class WarmBlooded extends Animal


{
// additional fields and methods specific to warm-blooded animals
}

public abstract class Equine extends WarmBlooded


{
// fields and methods specific to equines
}

public class Horse extends Equine


{
// fields and completed methods specific to horses
}

Here is a more computing-oriented example: Figure 13 shows a hierarchy that


might arise from listing the forms one would draw in a graphics window. The forms
that one actually draws reside (primarily) at the leaves of the tree; the non-leaf entries
are adjectives that list aspects or partial behaviors. (Although this rule is not hard
and fast—arbitrary Polygon objects are possible.)
The primary benefit of working with class hierarchies is that a hierarchy collects
together related classes, meaning that commonly used attributes and methods can be
written within abstract classes and shared by concrete classes. The major negative
aspect of a class hierarchy is its size—one actual object might be constructed by
extending many classes, which might be located in many different files. For example,
a Triangle object constructed from Figure 13 is a composite of classes Triangle,
Polygon, Shape, and Form; a programmer will quickly grow weary from reading four
(or more) distinct classes to understand the interface and internal structure of just
one object! If possible, limit the hierarchies you write to a depth of two or three
classes.
518

Figure 9.13: hierarchy of forms for drawing

Form

Point
Line

Straight
Jagged
Curved
Shape

Curved

Circle
Ellipse
Polygon

Triangle
Rectangle

A documentation tool, like javadoc, can generate helpful interface documentation


for a class hierarchy; see the section, “Generating Package APIs with javadoc,” which
follows.

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:

/** Form is the root of the geometric forms hierarchy */


public abstract class Form { }

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.)

2. Write taxonomies of the following:

(a) the people—teachers, students, and staff—who work in a school;


(b) the different forms of motorized vehicles (cars, trucks, cycles);
(c) forms of fruits and vegetables;
9.6. SUBTYPES VERSUS SUBCLASSES 519

(d) forms of music;


(e) forms of dance;
(f) computer input/output devices;
(g) people who work for a candy company (managers, chefs, assembly-line
workers, tasters, salespeople)

9.5.3 Frameworks and Abstract Classes


An abstract class is an “incomplete program,” and we might use a collection of
abstract classes to write a complex, powerful, but incomplete program that another
programmer finishes by writing one or two concrete classes.
A framework is such an incomplete program—it is a collection of classes and
interfaces organized into an architecture for a particular application area, such as
building graphics windows, or generating animations, or building spreadsheets, or
writing music, or creating card games. Some of the framework’s classes are left
abstract—incomplete. This is deliberate—a programmer uses the framework to build
a specific graphics window (or a specific animation, or spreadsheet, or song, or game)
by writing concrete classes that extend the abstract ones. The result is a complete
application that creates what the programmer desires with little effort.
For example, say that someone wrote for us a framework for building graphics
windows. Such a framework would contain classes that do the hard work of calcu-
lating colors, shapes, and sizes and displaying them on the computer console. Most
importantly, the framework would also contain an abstract class, say, by the name of
class GraphicsWindow, that already contains methods like setSize, and setVisible
but lacks a paint method. A programmer would use the framework and class
GraphicsWindow in particular to write concrete classes that extend GraphicsWindow
with paint methods. By using the framework, the programmer generates graphics
windows with minimal effort.
We have been doing something similar, of course, when we used Java’s Abstract
Window Toolkit (java.awt) and Swing (javax.swing) packages to build graphics win-
dows. These two packages form a framework for a range of graphics applications,
and by extending class JPanel, we “complete” the framework and create graphics
windows with little effort on our own part.
The next chapter surveys the AWT/Swing framework.

9.6 Subtypes versus Subclasses


It is time to review the crucial distinctions between Java interfaces and subclasses:
• An interface defines a behavioral specification or “connection point” between
classes or subassemblies that are developed separately. When a class implements
520

an interface, the class provides, with its methods, the behavior promised by the
interface.

• A subclass provides codings—implementations—of some methods that are omit-


ted from the superclass. When a class extends another, it inherits the existing
coded methods and provides codings for additional ones.
In a nutshell,
Interfaces list behaviors, subclasses list codings.
Therefore,
• Use an abstract class or a superclass when you are building a “family” of related
classes whose internal structures are similar; the superclass holds the coding
common to all the subclasses.

• 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.

9.7 class Object and Wrappers


In the section, “Class Hierarchies,” we saw how collections of classes are grouped into
hierarchies. The Java compiler forces such a hierarchy upon a user whether she desires
it or not: Within the package java.lang, there is a class Object, which defines basic
coding that all Java objects must have for construction in computer storage. The
Java compiler automatically attaches extends Object to every class that does not
already extend another.
For example, if we wrote
public class A {...}

public class B extends A {...}

the Java compiler treats the first class as if it were written


9.7. CLASS OBJECT AND WRAPPERS 521

public class A extends Object {...}

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:

public class Pair


{ Object[] r = new Object[2];

public Pair(Object ob1, Object ob2)


{ r[0] = ob1;
r[1] = ob2;
}

public Object getFirst()


{ return r[0]; }

public Object getSecond()


{ return r[1]; }
}

For example,

Pair p = new Pair(new JPanel(), new int[3]);


Object item = p.getFirst();
if ( item instanceof JPanel )
{ (JPanel)item.setVisible(true); }

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

Integer wrapped_int = new Integer(3);

this creates an object of type Integer that holds 3. Now, we might use it with class
Pair:

Pair p = new Pair(wrapped_int, new int[3]);


Object item = p.getFirst();
if ( item instanceof Integer )
{ System.out.println( ((Integer)item).intValue() + 4 ); }
522

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());
}

We receive this output:


3
abc
Pair@1f14c60

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
}

and Figure 3 is revised as follows:

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.)

9.8.1 Generating Package APIs with javadoc


Recall that a class’s Application Programming Interface (API) documents the class’s
interface in a readable format, say, as a web page. This can be done for a package,
also. Given a package, P, located in a folder of the same name, we can type at the
command line,
javadoc P

(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:

By clicking on the link for, say, BankAccountSpecification, we see the documentation


526

for the interface:

Exercise
Use javadoc to generate the API documentation for a package you have created.

9.9 Case Study: An Adventure Game


(Note: This section can be skipped on first reading.)
9.9. CASE STUDY: AN ADVENTURE GAME 527

Figure 9.14: Interfaces for an adventure game


/** RoomBehavior defines the behavior of a room */
public interface RoomBehavior
{ /** enter lets a player enter a room
* @param p - the player who wishes to enter
* @return whether the player sucessfully opened the door and entered. */
public boolean enter(PlayerBehavior p);

/** exit ejects a player from the room.


* @param p - the player who wishes to leave the room */
public void exit(PlayerBehavior p);

/** occupantOf returns the identity of the room’s occupant


* @return the address of the occupant object;
* if room unoccupied, return null */
public PlayerBehavior occupantOf();
}

/** PlayerBehavior defines the behavior of a player of an adventure game */


public interface PlayerBehavior
{ /** speak lets the player say one word
* @return the word */
public String speak();

/** explore attempts to enter a room and explore it


* @param r - the room that will be explored
* @return whether the room was successfully entered */
public boolean explore(RoomBehavior r);
}

We finish the chapter by applying interfaces and inheritance to design a complex


model whose components connect together in multiple, unpredictable ways: We are
building an “adventure game,” where players can enter and exit rooms. A player
enters an unoccupied room by speaking the “secret word” that opens the room’s
door. A player exits a room whenever she chooses.
We want the adventure game to be as general as possible, so we retrict as little as
possible the notions of “player” and “room.” To start, we write specifications (Java
interfaces) that state the minimal expected behaviors of rooms and players. The
interfaces might appear as in Figure 14. The two interfaces depend on each other for
stating their respective behaviors, so the two interfaces must be compiled together.
(To do this, place the files, RoomBehavior.java and PlayerBehavior.java in the same
folder and compile one of them; the Java compiler will automatically compile both.)
528

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,

boolean went_inside = r.enter(this);

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:

RoomBehavior[] ground_floor = new RoomBehavior[4];


ground_floor[0] = new BasicRoom("kitchen", "pasta");
ground_floor[3] = new BasicRoom("lounge", "swordfish");

Explorer harpo = new Explorer("Harpo Marx", "swordfish");


Explorer chico = new Explorer("Chico Marx", "tomato");
boolean success = harpo.explore(ground_floor[3]);

Upon completion of the last statment, where the harpo.explore method is invoked,
9.9. CASE STUDY: AN ADVENTURE GAME 529

Figure 9.15: room for an adventure game


/** BasicRoom models a room that can have at most one resident at a time */
public class BasicRoom implements RoomBehavior
{ private PlayerBehavior occupant; // who is inside the room at the moment
private String rooms name;
private String secret word; // the password for room entry

/** Constructor BasicRoom builds the room.


* @param name - the room’s name
* @param password - the secret word for entry into the room */
public BasicRoom(String name, String password)
{ occupant = null; // no one is in the room initially
rooms name = name;
secret word = password;
}

public boolean enter(PlayerBehavior p)


{ boolean result = false;
if ( occupant == null && secret word.equals(p.speak()) )
{ occupant = p;
result = true;
}
return result;
}

public void exit(PlayerBehavior p)


{ if ( occupant == p ) // is p indeed in this room at the moment?
{ occupant = null; }
}

public PlayerBehavior occupantOf()


{ return occupant; }
}
530

Figure 9.16: a player that can explore rooms


/** Explorer models a player who explores rooms */
public class Explorer implements PlayerBehavior
{ private String my name;
private String my secret word; // a password for entering rooms
private RoomBehavior where I am now; // the room this object occupies

/** Constructor Explorer builds the Player


* @param name - the player’s name
* @param word - the password the player can speak */
public Explorer(String name, String word)
{ my name = name;
my secret word = word;
where I am now = null; // player does not start inside any room
}

public String speak()


{ return my secret word; }

/** 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;
}
}

public boolean explore(RoomBehavior r)


{ if ( where I am now != null )
{ exitRoom(); } // exit current room to go to room r:
boolean went inside = r.enter(this); // ‘‘this’’ means ‘‘this object’’
if ( went inside )
{ where I am now = r; }
return went inside;
}

/** locationOf returns the room that is occupied by this player */


public RoomBehavior locationOf()
{ return where I am now; }
}
9.9. CASE STUDY: AN ADVENTURE GAME 531

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 PlayerBehavior occupant ==


null private PlayerBehavior occupant ==
a4

private String rooms name ==


"kitchen" private String rooms name ==
"lounge"

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

private String my name ==


"Harpo Marx" private String my name ==
"Chico Marx"

private String my secret word ==


"swordfish" private String my secret word ==
"tomato"

private RoomBehavior where I am now == a3 private RoomBehavior where I am now == null


public String speak() public String speak()
public void exitRoom() public void exitRoom()
public boolean explore(RoomBehavior r) public boolean explore(RoomBehavior r)
public RoomBehavior locationOf() public RoomBehavior locationOf()

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:

class Dungeon implements RoomBehavior

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

Figure 9.17: interface for treasures


/** TreasureProperty defines a treasure object */
public interface TreasureProperty
{ /** contentsOf explains what the treasure is
* @return the explanation */
public String contentsOf();
}

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

Explorer harpo = new Explorer("Harpo Marx", "swordfish");

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:

/** locationOf returns the room that is occupied by this player */


public RoomBehavior locationOf();

Now, rewrite the enter method of BasicRoom.

9.9.1 Interfaces and Inheritance Together


Classes BasicRoom and Explorer are too simplistic for an interesting game. We might
design rooms that contain “treasures” so that players can take the treasures from the
rooms they enter.
Since a treasure might be a variety of things, it is simplest to define an interface
that gives the basic property of being a treasure. See Figure 17. Next, we can define
an interface that defines what it means for an object to hold a treasure. We might
write the interface in Figure 18.
A room that possesses an entry and exit as well as a treasure must implement
both interface RoomBehavior as well as interface Treasury. It is indeed acceptable
for one class to implement two interfaces, and it might look like this:
9.9. CASE STUDY: AN ADVENTURE GAME 533

Figure 9.18: interface for a treasury


/** Treasury describes the method an object has for yielding
* a treasure */
public interface Treasury
{ /** yieldTreasure surrenders the room’s treasure
* @param p - the player who requests the treasure
* @return the treasure (or null, if the treasure is already taken) */
public TreasureProperty yieldTreasure(PlayerBehavior p);
}

public class Vault implements RoomBehavior, Treasury


{ private PlayerBehavior occupant; // who is inside the room at the moment
private String rooms_name;
private String secret_word; // the password for room entry

private TreasureProperty valuable; // the treasure item held in this room

/** Constructor Vault builds the room.


* @param name - the room’s name
* @param password - the secret word for entry into the room
* @param item - the treasure to be saved in the room */
public Vault(String name, String password, TreasureProperty item)
{ occupant = null;
rooms_name = name;
secret_word = password;
valuable = item;
}

public boolean enter(PlayerBehavior p)


{ ... } // insert coding from Figure 15

public void exit(PlayerBehavor p)


{ ... } // insert coding from Figure 15

public PlayerBehavior occupantOf()


{ ... } // insert coding from Figure 15

public TreasureProperty yieldTreasure(PlayerBehavior p)


{ TreasureProperty answer = null;
if ( p == occupant )
{ answer = valuable;
valuable = null;
534

Figure 9.19: VaultRoom defined by inheritance


/** VaultRoom is a room that holds a treasure---it is constructed from
* class BasicRoom, by means of inheritance, plus the structure below. */
public class VaultRoom extends BasicRoom implements Treasury
// because BasicRoom implements RoomBehavior, so does VaultRoom
{ private TreasureProperty valuable; // the treasure item held in this room

/** Constructor VaultRoom builds the room.


* @param name - the room’s name
* @param password - the secret word for entry into the room
* @param item - the treasure to be saved in the room */
public VaultRoom(String name, String password, TreasureProperty item)
{ // first, invoke class BasicRoom’s constructor method:
super(name, password); // ‘‘super’’ means ‘‘superclass’’
valuable = item;
}

public TreasureProperty yieldTreasure(PlayerBehavior p)


{ TreasureProperty answer = null;
if ( p == occupantOf() ) // invokes the occupantOf method in BasicRoom
// You can also state, super.occupantOf()
// Another format is, this.occupantOf()
{ answer = valuable;
valuable = null;
}
return answer;
}
}

}
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");

TreasureProperty the_treasure = new Jewel("diamond");


VaultRoom b = new VaultRoom("The Vault", "Open, please!", the_treasure);
where we define class Jewel this simply:
public class Jewel implements TreasureProperty
{ private String name; // the name of the jewel

public Jewel(String id)


{ name = id; }

public String contentsOf()


{ return name; }
}
536

Here is a picture of storage after the statements execute:


a1 : BasicRoom
BasicRoom a == a1 private PlayerBehavior occupant ==
null

TreasureProperty the treasure == a2 private String rooms name ==


"The Lounge"

VaultRoom b == a3 private String secret word == "Hello"


public boolean enter(PlayerBehavior p) { ... }
public void exit(PlayerBehavior p) { ... }
public PlayerBehavior occupantOf() { ... }
a2 : Jewel

private String name == "diamond"


public String contentsOf() { ... }

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:

private PlayerBehavior occupant ==


null

private String rooms name ==


"The Vault"

private String secret word == "Open, Please!"


public boolean enter(PlayerBehavior p) { ... }
public void exit(PlayerBehavior p) { ... }
public PlayerBehavior occupantOf() { ... }

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

Figure 9.20: diagram for model of adventure game

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:

public class GoldCoin 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:

/** TreasureHunterBehavior describes the behavior of a player that tries


* to take treasures */
public interface TreasureHunterBehavior
{ /** takeTreasure tries to extract the treasure from the current room
* that this player occupies.
* @return true if the treasure was succesfully extracted from the
* room and saved by this player; return false otherwise */
public boolean takeTreasure();
538

/** getTreasures returns an array of all the treasures located so far


* by this player
* @return the array of treasures */
public TreasureProperty[] getTreasures();
}

Write this class:

/** 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:

public class RuthlessHunter extends TreasureHunter


{ ...

public boolean explore(RoomBehavior r)


{ ... }
}

When we construct a RuthlessHunter, e.g.,

VaultRoom bank = new VaultRoom("Bank", "bah!", new Jewel("ruby"));


RuthlessHunter bart = new RuthlessHunter("Black Bart", "bah!");
bart.explore(bank);

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

Explorer indiana = new Explorer("Indiana Jones", "hello");


indiana.explore(bank);

Which version of explore method executes? Method overriding is studied in


the “Beyond the Basics” section at the end of this Chapter.

9.9.2 Inheritance of Interfaces


Interfaces can be connected by inheritance, just like classes are. For example, per-
haps we want to ensure that any object that has Treasury behavior must also have
RoomBehavior. This can be enforced by changing the interface for the Treasury to
inherit RoomBehavior:

/** TreasuryBehavior describes a room that holds a treasure */


public interface TreasuryBehavior extends RoomBehavior
{ /** yieldTreasure surrenders the room’s treasure
* @param p - the player who requests the treasure
* @return the treasure (or null, if the treasure is already taken) */
public TreasureProperty yieldTreasure(PlayerBehavior p);
}

Because it extends RoomBehavior, interface TreasuryBehavior requires all the meth-


ods of RoomBehavior plus its own. It so happens that class VaultRoom in Figure
13, because it extends BasicRoom, also is capable of implementing TreasuryBehavior.
Indeed, we can change its header line accordingly:

public class VaultRoom extends BasicRoom implements Treasury, TreasuryBehavior

of course, if we no longer use interface Treasury in the game’s architecture, we can


shorten the header line to read merely

public class VaultRoom extends BasicRoom implements TreasuryBehavior

Because TreasuryBehavior extends RoomBehavior, the expected subtyping rela-


tion is established: TreasuryBehavior <= RoomBehavior. The Java compiler uses the
subtyping to check compatibility of classes to interfaces.

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:

• interface (from Figure 1):

public interface BankAccountSpecification


{ public void deposit(int amount);
public boolean withdraw(int amount);
}

• inheritance (from Figure 10):

public class HumanPlayer extends CardPlayer


{ public HumanPlayer(int max_cards)
{ super(max_cards); }

public boolean wantsACard()


{ String response = JOptionPane.showInputDialog
("Do you want another card (Y or N)?");
return response.equals("Y");
}
}

• abstract class (from Figure 9):

public abstract class CardPlayer implements CardPlayerBehavior


{ private Card[] my_hand;
private int card_count;

public CardPlayer(int max_cards)


{ my_hand = new Card[max_cards];
card_count = 0;
}

public abstract boolean wantsACard(); // method will be written later

public void receiveCard(Card c)


{ my_hand[card_count] = c;
card_count = card_count + 1;
}
}

• 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:

Record mystery_record = db.find(mystery_key);


if ( mystery_record instanceof BasicPerson )
{ System.out.println( ((BasicPerson)mystery_record).nameOf() ); }
else { System.out.println("unknown record type"); }

• super:

public class PersonAddress extends Person


{ private String address;

public PersonAddress(String the_name, String the_addr)


{ super(the_name); // this gives the_name to Person’s constructor
address = the_addr;
}

public String getAddress()


{ return address; }
}

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.

• implementing an interface: writing a class that contains methods whose header


lines match the ones named in the interface.

• 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.

• concrete class: a “normal” class, all of whose methods have bodies.


542

• package: a collection of classes that are grouped together in a folder and labelled
with a common name.

• subtyping relationship: a relationship between two data types; We write C <= D


to state that C is a subtype of D, meaning that C-typed values can be used in
any context where a D-typed values is expected.

• 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.

• class hierarchy: a collection of abstract and concrete classes, arranged by their


super/subclass relationships, usually depicted as a tree structure.

• framework: a collection of classes designed to be augmented with only a few


additional classes to construct complete applications in a problem domain.

• class Object: a built-in Java class that is automatically a superclass to all


other classes.

• 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 class implements an interface by having methods whose header lines match


the ones named in the interface.

• 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.

9.11 Programming Projects


1. Return to an application that you wrote in response to a Programming Project
in Chapter 7 or 8. Redesign and reimplement the application with the assistance
of interfaces and abstract classes. (If you did not work a substantial project
from either of those two chapters, then build the library database application
described in Project 6, Chapter 8.)

2. Build a computerized “adventure game,” where one or more players explore


rooms and collect treasures. Rooms are entered and exited through doors,
and every door connects to a passageway, which itself leads to zero or more
doors to other rooms. To orient herself, a player can ask a room its name.
Invent additional rules for the game (e.g., a player can leave behind in a room
a “message” for another player to find; treasures have point values; a player
“loses energy” as it goes from room to room and must find “food” to eat in a
room to regain energy, etc.)

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:

• an individual’s name or a business’s name or a name that might be either.


(The program prints the patrons with that surname and their telephone
numbers.)
544

• 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.

6. Construct a “framework” to help a programmer build card games where one


human player interacts with a dealer and competes against zero or more com-
puterized players.

7. One of the Projects in Chapter 8 suggested that you implement a database


program for maintaining a sales inventory. Each sales item had this associated
information: item’s name, id number, wholesale price, retail price, and quantity
in stock.
Extend this problem as follows: Say that the inventory is for an automobile
company (or computer company or bicycle company or ...) that services sev-
eral, related models of car (computer, bicycle). The parts inventory must be
organized so that each part is associated with the models of car that use it. De-
sign a database that implements the parts inventory so that a user can obtain
(among other output data), for a specific model of car, the portions of a car,
the parts contained in each portion, and the prices of all the parts.

9.12 Beyond the Basics


9.12.1 Subclasses and Method Overriding

9.12.2 Semantics of Overriding

9.12.3 final components

9.12.4 Method Overloading


9.12. BEYOND THE BASICS 545

9.12.5 Semantics of Overloading

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

9.12.1 Subclasses and Method Overriding


As noted earlier in this chapter, we used inheritance to construct graphics panels:
public class MyPanel extends JPanel
{ ...
public void paintComponent(Graphics g) { ... }
...
}

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() { }

public int add(int i, int j)


{ return i + j; }
}

we can extend C and override its method:


public class D extends C
{ public D() { }

public int add(int i, int j)


{ return i * j; }
546

public int sub(in i, int j)


{ return i / j; }
}

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));

6 and then 5 appear on the display.


Method overriding is intended for “method improvement,” and the situation with
JPanel’s paintComponent method is typical: An existing class has an unexciting
method that can be improved within a subclass. Method override is also used to
make a subclass’s method “more intelligent” by using fields that are declared only in
the subclass. But method overriding can be complex, so we study more examples.
Figure 21 shows two classes that model bank accounts. A BasicAccount allows
only deposits (an escrow account, which holds one’s loan payment money, might be
an example); a CheckingAccount (“current” account) is a BasicAccount extended with
the ability to make withdrawals.
There is a problem: CheckingAccount’s withdraw method must alter the private
balance field of BasicAccount. Java allows such behavior, if the field in the superclass
is relabelled as protected. (Public access is still disallowed of a protected field, but
subclasses—and alas, other classes in the same package as these two—can alter the
field.)
This arrangement is not completely satisfactory, because it means that class
CheckingAccount depends on the internals of class BasicAccount, so our freedom to
alter and improve the components of BasicAccount is restricted. (Some people say
this situation “breaks encapsulation,” because the internals are “exposed” to another
class.) It might be be better to redesign the classes in the Figure so that they both
extend an abstract class, but we press on.
Next, Figure 22 introduces a class AccountWithInterest that models a checking
account that pays interest, provided that the balance in the account never falls below
a stated minimum. Here, the withdraw method must be “more intelligent” and mon-
itor whether a withdrawal causes the account’s balance to fall below the minimum
amount for interest payment, and the new version of withdraw overrides the one in
the superclass.
The body of the new, overriding method uses the invocation, super.withdraw(amount),
to make the physical removal of the amount: The keyword, super, forces the method
9.12. BEYOND THE BASICS 547

Figure 9.21: two classes for bank accounts


/** BasicAccount is a bank account that holds money */
public class BasicAccount
{ protected int balance; // the money; note the keyword, ‘‘protected’’

/** BasicAccount creates the account


* @param initial amount - the starting balance */
public BasicAccount(int initial amount)
{ balance = initial amount; }

/** deposit adds money to the account


* @param amount - the amount to be deposited */
public void deposit(int amount)
{ if ( amount > 0 )
{ balance = balance + amount; }
}

/** balanceOf returns the current balance


* @return the balance */
public int balanceOf()
{ return balance; }
}

/** CheckingAccount is a basic account from which withdrawals can be made */


public class CheckingAccount extends BasicAccount
{
/** CheckingAccount creates the account
* @param initial amount - the starting balance */
public CheckingAccount(int initial amount)
{ super(initial amount); }

/** withdraw removes money from the account, if possible


* @param amount - the amount to be removed
* @return true, only if the balance is enough to make the withdrawal */
public boolean withdraw(int amount)
{ boolean outcome = false;
if ( amount <= balance )
{ balance = balance - amount;
outcome = true;
}
return outcome;
}
}
548

Figure 9.22: checking account with interest


/** AccountWithInterest models an interest bearing checking account */
public class AccountWithInterest extends CheckingAccount
{ int minimum balance; // the amount required to generate an interest payment
boolean eligible for interest; // whether the account has maintained
// the minimum balance for this time period

/** AccountWithInterest creates the account


* @param required minimum - the minimum balance that must be maintained
* to qualify for an interest payment
* @param initial amount - the starting balance */
public AccountWithInterest(int required minimum, int initial balance)
{ super(initial balance);
minimum balance = required minimum;
eligible for interest = (initial balance > minimum balance);
}

/** withdraw removes money from the account, if possible


* @param amount - the amount to be removed
* @return true, only if the balance is enough to make the withdrawal */
public boolean withdraw(int amount)
{ boolean ok = super.withdraw(amount);
eligible for interest = eligible for interest // is minimum maintained?
&& balanceOf() > minimum balance;
return ok;
}

/** computeInterest deposits an interest payment, if the account qualifies


* @param interest rate - the rate of interest, e.g., 0.05 for 5%
* @return whether the account qualified for an interest payment. */
public boolean computeInterest(double interest rate)
{ boolean outcome = false;
if ( eligible for interest )
{ int interest = (int)(balanceOf() * interest rate);
deposit(interest);
outcome = true;
}
eligible for interest = (balanceOf() > minimum balance); // reset
return outcome;
}
}
9.12. BEYOND THE BASICS 549

in the superclass—here, method withdraw in CheckingAccount—to be used. In this


way, the new withdraw method exploits the behavior of the method it overrides.
The override of withdraw in Figure 22 is acceptable programming style, because it
maintains the same responsibility of the withdraw method in Figure 21—to withdraw
money—but does so in a more intelligent way.
The classes in Figures 21 and 22 let us create a variety of different bank accounts
simultaneously, e.g.,
BasicAccount b = new BasicAccount(100);
CheckingAccount c = new BasicAccount(0);
AccountWithInterest i = new AccountWithInterest(2500, 4000);

creates these objects:

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

int minimum balance ==


2500

boolean eligible for interest ==


true

public boolean withdraw(int amount) {... super.withdraw(amount) ...}


public boolean computeInterest(double interest rate) {...}
// from CheckingAccount:
public boolean withdraw(int amount) {...}
// from BasicAccount:
int balance == 4000
public void deposit(int amount) {...}
public int balanceOf() {...}

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

9.12.2 Semantics of Overriding


The semantics of method override possesses a surprising feature: The method that
the Java compiler selects as the receiver of an invocation might be different from the
method that is selected when the invocation is executed!
You can avoid reading this section if you promise never to override a superclass’s
method that is invoked by other methods in the superclass. That is, don’t do this:
public class C1
{ ...
public ... f(...)
{ ... }

public ... g(...)


{ ... f(...) ... }
}

public class C2 extends C1


{ ...
public ... f(...)
{ ... }
}

This form of example causes surprising behavior when g is invoked.


To understand why, we start by reviewing the main points from the section, “For-
mal Description of Methods,” at the end of Chapter 5. There, we learned that the
Java compiler checks the data typing of every method invocation by selecting the
method that is the target of the invocation. For simplicity, we consider normal (non-
static) invocations. As described in Chapter 5, data-type checking is a four-step
process:
Given an invocation,
[[ RECEIVER . ]]? NAME0 ( EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn)

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

VISIBILITY TYPE0 NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)


9.12. BEYOND THE BASICS 551

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).

3. attaches the header-line information to the invocation. The method invocation


now looks like this:

[[ RECEIVER . ]]? NAME0 (EXPRESSION1: TYPE1, EXPRESSION2: TYPE2,


..., EXPRESSIONn: TYPEn) SUFFIX;

where SUFFIX is one of


• public: the selected method is a public method
• private Ck: the selected method is a private method that resides in class
Ck
• super Ck: the RECEIVER is super, and the selected method is a public
method that resides in class Ck
4. returns TYPE0 as the result type of the invocation.
When a method invocation is executed, the Java Virtual Machine uses the typing
information attached by the Java compiler attached to locate the invoked method.
As stated in Chapter 5, given the annotated invocation,
[[ RECEIVER . ]]? NAME0 ( EXPRESSION1 : TYPE1, EXPRESSION2 : TYPE2,
..., EXPRESSIONn : TYPEn ) SUFFIX

the execution follows these five steps:


1. RECEIVER computes to an address of an object, a. (Note: If RECEIVER is omitted,
or is super or this, use the address of the object in which this invocation is
executing.)
2. Within the object at address a, select the method NAME0. This step depends on
the value of the SUFFIX:
• If it is private Ck, select the private method that came from class Ck
whose header line has this form:
private ... NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)

• 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

public ... NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)

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.

3. Evaluate the actual parameters.

4. Bind the actual parameters to the formal parameters.

5. Execute the body of the selected method.

Step 2 is of interest to us when an invocation’s SUFFIX is marked public. In this


case, the Java interpreter makes a search for a method named NAME0, and the method
that is selected might be different from the one that the Java compiler selected. We
see this in the example that follows.
Figure 23 defines two classes, C1 and C2, that hold integers and report their values.
Since the second class, C2, augments the integer held in its superclass, C1, with one
of its own, it overrides the stringVal function so that both integers are included
in the answer returned by the function. This raises an interesting question: Which
version of stringVal will be invoked within print? The answer depends on the object
created. For this case,

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

Figure 9.23: example of method override


public class C1
{ private int i;

public C1(int x)
{ i = x; }

public String stringVal()


{ return f(i); }

public void print()


{ System.out.println( "value is " + stringVal() ); }

private String f(int x)


{ return "C1: " + x; }
}

public class C2 extends C1


{ private int j;

public C2(int x, int y)


{ super(x);
j = y;
}

public String stringVal()


{ return super.stringVal() + f(j) ; }

private String f(int x)


{ return " C2: " + x; }
}
554

compiler attached to each method invocation:


a2 : C2
C2 c2 == a2 5
private int j ==
public String stringVal()
{ return (super.stringVal() super C1) + (f(j: int) private C2); }
private String f(int x) { return " C2: " + x; }
// from class C1:
private int i == 4
public String stringVal()
{ return (f(i: int) private C1); }
public void print()
{ System.out.println( "value is " + (stringVal() public) ); }
private String f(int x) { return "C1: " + 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.

9.12.3 final components


You can label a class or any of its components as final, if you wish to prohibit modi-
fications to it.
A final class is labelled,
public final class C
{ ... }

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;

public final String stringVal()


{ return f(i); }

public void print()


{ System.out.println( "value is " + stringVal() ); }

private String f(int x)


{ return "C1: " + x; }
}

This makes it impossible to override stringVal in class C2 (and likely forces C2 to


override the print method, which is a more benign override).
A third use of final is to restrict a variable so that once it is initialized, it can
not be altered. A simple example is,
private final int MAX_SIZE = 50;

which permanently fixes the value of MAX SIZE to 50.


A final variable can be initialized from within a constructor:
556

public class FrozenBankAccount


{ private final int balance;

public FrozenBankAccount(int final_balance)


{ balance = final_balance; }

... // here, assignments to balance are disallowed


}

9.12.4 Method Overloading


In Chapter 6, Figure 15, we saw two methods in class BankWriter with the same
name:
/** BankWriter writes bank transactions */
public class BankWriter extends JPanel
{ ...

/** showTransaction displays the result of a monetary bank transaction


* @param message - the transaction
* @param amount - the amount of the transaction */
public void showTransaction(String message, int amount)
{ last_transaction = message + " " + unconvert(amount);
repaint();
}

/** showTransaction displays the result of a bank transation


* @param message - the transaction */
public void showTransaction(String message)
{ last_transaction = message;
repaint();
}
}

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 class BankAccount


{ private int balance;

public BankAccount()
{ balance = 0; }

public BankAccount(int initial_balance)


{ balance = initial_balance; }

...
}

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:

public void displayValue(int i)


{ System.out.println(i); }

public void displayValue(String s)


{ System.out.println(s); }

public void displayValue(BankAccount b)


{ System.out.println(b.balanceOf()); }

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

public void println() {...}


public void println(boolean x) {...}
public void println(char x) {...}
public void println(int x) {...}
public void println(long x) {...}
public void println(double x) {...}
558

and so on. (Consult the Java API for details.)


In the case of println, we note that some of the data types used in the overloaded
method are related by subtyping. For example, int is a subtype of long and of double.
This suggests that any of the three println methods that can print integers, longs,
or doubles, can be used to execute System.out.println(3). But the method, public
void println(int x) {...} is selected because it is the best matching method for
the invocation.
But this approach gets quickly confusing. Say that the name, f, is overloaded by
these two methods:

public boolean f(int x)


{ System.out.println("nonfractional");
return true;
}

public boolean f(double x)


{ System.out.println("fractional");
return false;
}

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

OneInt s = new OneInt(5);


TwoInt t = new TwoInt(5, 2);

we get these two objects in storage:


a2 : TwoInt
OneInt s == a1 TwoInt t == a2 2
private int j ==
public int secondVal() { return j; }
a1 : OneInt // from class OneInt:

private int i == 5 private int i == 5


public int firstVal() { return i; } public int firstVal() { return i; }

Say that, in a third class, we write these two methods:


9.12. BEYOND THE BASICS 559

Figure 9.24: one class that extends another


/** OneInt is a ‘‘wrapper’’ for a single integer */
public class OneInt
{ private int i;

public OneInt(int x) { i = x; }

public int firstVal() { return i; }


}

/** TwoInt is a ‘‘wrapper’’ for a pair of integers. */


public class TwoInt extends OneInt
{ private int j;

public TwoInt(int x, int y)


{ super(x); // execute the constructor for OneInt, the superclass
j = y;
}

public int secondVal() { return j; }


}

public boolean equals(OneInt x, OneInt y)


{ return (x.firstVal() == y.firstVal()); }

public boolean equals(TwoInt x, TwoInt y)


{ return (x.firstVal() == y.firstVal()) && (x.secondVal() == y.secondVal()) }

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; }

public boolean equals(TwoInt x, OneInt y)


{ return false; }
560

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)

• it is acceptable to overload a method name based on the data type of a pa-


rameter, if the data types are unrelated by subtyping (cf. the displayValue
example)
but it is questionable to overload based on parameter data types related by subtyping
(cf. the equals example).
In many cases, what appears to be an “essential” use of overloading based on
parameter data type can be eliminated by augmenting the classes involved with an
abstract class. For example, we can retain all four of the above variants of equals if
we change, in a trivial way, the relationship between classes OneInt and TwoInt with
an abstract class:
public abstract class AbsInt
{ private int i;

public AbsInt(int x) { i = x; }

public int firstVal() { return i; }


}

public class OneInt extends AbsInt


{ public OneInt(int x)
{ super(x); }
}

public class TwoInt extends AbsInt


{ private int j;

public TwoInt(int x, int y)


{ super(x);
j = y;
9.12. BEYOND THE BASICS 561

public int secondVal() { return j; }


}

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.

9.12.5 Semantics of Overloading


The Java compiler must type check overloaded method names, like the ones just seen.
First, you should review the section, “Formal Description of Methods,” in Chapter 5,
and study closely the definition of “best matching method.” This definition must be
revised to handle the more difficult examples in the previous section.
You can avoid reading this section if if you promise never to overload a method
name based on parameter data types related by subtyping.

Revised Definition of Best Matching Method


Say that the Java compiler must locate the best matching method for this invocation:

[[ RECEIVER NAME0 . ]]? (EXPRESSION1, EXPRESSION2, ..., EXPRESSIONn)

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,

... ... NAME0(TYPE1 NAME1, TYPE2 NAME2, ..., TYPEn NAMEn)

such that each Ti is a subtype of TYPEi, for all i in the range of 1..n.

• If exactly one such method definition of NAME0 in C0 is found, then this


method is selected.
562

• 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:

public boolean equals(OneInt x, OneInt y)


{ return (x.firstVal() == y.firstVal()); }

public boolean equals(TwoInt x, TwoInt y)


{ return (x.firstVal() == y.firstVal()) && (x.secondVal() == y.secondVal()) }

public boolean equals(OneInt x, TwoInt y)


{ return false; }

OneInt s = new OneInt(5);


TwoInt t = new TwoInt(5, 2);
System.out.println(equals(s, t));

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() { }

public void f(int i) { System.out.println("A"); }


}

public class B extends A


{ public B() { }

public void f(double i) { System.out.println("B"); }


}
...
B ob = new B();
ob.f(3);

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

Graphical User Interfaces and Event-


Driven Programming
10.1 Model-View-Controller Revisited

10.2 Events

10.3 The AWT/Swing Class Hierarchy

10.4 Simple Windows: Labels and Buttons

10.5 Handling an Event

10.5.1 A View as Action Listener

10.5.2 A Separate Controller

10.5.3 A Button-Controller

10.6 Richer Layout: Panels and Borders

10.6.1 An Animation in a Panel

10.7 Grid Layout

10.8 Scrolling Lists

10.9 Text Fields

10.10 Error Reporting with Dialogs

10.11 TextAreas and Menus

10.11.1 Case Study: Text Editor

10.12 Event-Driven Programming with Observers

10.12.1 Observers and the MVC-Architecture

10.13 Summary

10.14 Programming Projects

10.15 Beyond the Basics


10.1. MODEL-VIEW-CONTROLLER REVISITED 565

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.

10.1 Model-View-Controller Revisited


The model-view-controller (MVC) architecture we have used for our programs was
first developed to manage programs with GUIs. The philosophy and terminology
behind MVC might be explained as follows:
• A computer program is a kind of “appliance,” like a television set, radio, or
hand-held calculator, so it should look like one: The program should have a
view or appearance like an appliance, so that its human user feels familiar with
it.

• Appliances have controls—switches, buttons, knobs, sliders—that their users


adjust to operate it. A program should also have controllers that its user adjusts
to execute the program. And for familiarity’s sake, the controllers should have
the appearance of switches, buttons, knobs, etc.

• An apppliance’s controls are connected to the appliance’s internal circuitry,


which does the work that its user intended. Similarly, a program’s controllers are
connected to the program’s model, which calculates results that are portrayed by
the program’s view. By “portrayed,” we mean that the view presents a picture
of the internal state of the model. Indeed, a program’s model might even have
multiple views that are presented simultaneously.
The manner in which the model, view, and controller collaborate is equally impor-
tant: A user interacts with a program’s view, say, by adjusting one of its controllers.
This awakens the controller, which might examine the view for additional data and
then send messages to the program’s model. The model executes the controller’s
messages, computing results and updating its internal state. The view is then sent a
566

Figure 10.1: model-view-controller architecture

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.

10.3 The AWT/Swing Class Hierarchy


Before we write GUIs with Java’s AWT/Swing framework, we must survey the com-
ponents provided by the framework. We begin with some terminology.
An entity that can have a position and size (on the display screen) and can have
events occur within it is called a component. A component that can hold other
components is a container; a panel is the standard container into which one inserts
components (including painted text and shapes). Panels are themselves inserted into
a container called a window, which is a “top-level” container, that is, a container that
can be displayed by itself.
A frame is a window with a title and menus; frames are “permanent” in that they
are created when an application starts and are meant to exist as long as the application
10.3. THE AWT/SWING CLASS HIERARCHY 569

Figure 10.2: frame with basic components

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

• a text component, into which a user can type text

• a button, which can be pushed, triggering an action event

• a list of items, whose items can be chosen (“selected”)

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

Figure 10.3: sample dialog

Figure 10.4: frame with menus


10.4. SIMPLE WINDOWS: LABELS AND BUTTONS 571

• 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

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.

2. Several of the classes in Figure 5 have methods named addSOMETHINGListener.


The classes that possess such methods are capable of generating SOMETHING
events. List the classes that generate events.

10.4 Simple Windows: Labels and Buttons


The standard graphical user interface for a program is a frame, generated from class
JFrame. We made extensive use of JFrame in previous chapters, using it to hold and
display panels. Now, we learn how to insert components like labels and buttons into
a frame.
572

Figure 10.5: partial AWT/Swing hierarchy


Object
|
+-Component [abstract]: setSize(int,int), setVisible(boolean), setFont(Font),
| isShowing():boolean, getLocationOnScreen():Point, setLocation(Point),
| paint(Graphics), repaint(), setForeground(Color),
| setBackground(Color), getGraphics()
|
+-Container: add(Component), add(Component,Object), setLayout(LayoutManager)
|
+-Window: pack(), dispose(), addWindowListener(WindowListener)
| |
| +-JFrame: JFrame(), setTitle(String), setJMenuBar(JMenuBar)
| getContentPane():Container
|
+-JApplet: JApplet(), init(), getParameter(String):String,
| getContentPane():Container, setJMenuBar(JMenuBar)
|
+-JComponent [abstract]: paintComponent(Graphics)
|
+-AbstractButton [abstract]: addActionListener(ActionListener),
| | setEnabled(Boolean), getText():String, setText(String),
| | setSelected(Boolean), isEnabled():boolean,
| | isSelected():boolean, doClick()
| |
| +-JButton: JButton(String), JButton(Icon), JButton(String, Icon),
| | setIcon(Icon)
| |
| +-JMenuItem: JMenuItem(String)
| |
| +-JMenu: JMenu(String), add(Component), addSeparator()
|
+-JLabel: JLabel(String), getText():String, setText(String)
|
+-JList: JList(Object[]),getSelectedIndex():int,setSelectedIndex(int),
| getSelectedIndices():int[], setSelectedIndices(int[]),
| setSelectionMode(int), clearSelection(),
| addListSelectionListener(ListSelectionListener)
|
+-JMenuBar: JMenuBar(), add(JMenu)
|
+-JOptionPane: showMessageDialog(Component,Object),
| showConfirmDialog(Component,Object):int,
| showInputDialog(Component,Object):String
...
10.4. SIMPLE WINDOWS: LABELS AND BUTTONS 573

Figure 10.5: partial AWT/Swing hierarchy (concl.)


|
+-JPanel: Panel(), Panel(LayoutManager)
|
+-JScrollPane: JScrollPane(Component)
|
+-JTextComponent [abstract]: cut(), paste(), copy(), getText():String,
| setText(String), getSelectionStart():int,
| getSelectionEnd():int, getSelectedText():String,
| replaceSelection(String), getCaretPosition():int,
| setCaretPosition(int), moveCaretPosition(int),
| isEditable():boolean, setEditable(boolean)
|
+-JTextField: JTextField(String,int),
| addActionListener(ActionListener)
+-JTextArea: JTextArea(String,int,int), insert(String,int)
replaceRange(String,int,int), setLineWrap(boolean)

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,

which is produced by the program in Figure 8.


Let’s examine the statements in the constructor method one by one:

• JLabel label = new JLabel("Press This:") constructs a label object, label


that displays the string, Press This:.

• Similarly, JButton button = new JButton("OK") constructs a button.

• 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

Figure 10.6: points, fonts, images, layouts, and events


Object
|
+-BorderLayout: BorderLayout(), BorderLayout.NORTH, BorderLayout.SOUTH,
| BorderLayout.EAST, BorderLayout.WEST, BorderLayout.CENTER
|
+-FlowLayout: FlowLayout(), FlowLayout(int), FlowLayout.LEFT,
| FlowLayout.RIGHT, FlowLayout.CENTER
|
+-GridLayout: GridLayout(int,int)
|
+-Font: Font(String,int,int), Font.PLAIN, Font.BOLD, Font.ITALIC
|
+-Point: Point(int,int), translate(int,int)
|
+-EventObject
| |
| +-AWTEvent [abstract]
| | |
| | +-ActionEvent: getActionCommand():String, getSource():Object
| | |
| | +-WindowEvent
| |
| +-ListSelectionEvent
|
+-WindowAdapter [implements WindowListener]
|
+-Image
|
+-ImageIcon [implements Icon]: ImageIcon(String), getImage():Image
|
+-Observable: addObserver(Observer), setChanged(), notifyObservers(),
notifyObservers(Object)
10.4. SIMPLE WINDOWS: LABELS AND BUTTONS 575

Figure 10.7: interfaces


public interface ActionListener
{ public void actionPerformed(ActionEvent e); }

public interface WindowListener


{ public void windowActivated(WindowEvent e);
public void windowClosed(WindowEvent e);
public void windowClosing(WindowEvent e);
public void windowDeactivated(WindowEvent e);
public void windowDeiconified(WindowEvent e);
public void windowIconified(WindowEvent e);
public void windowOpened(WindowEvent e);
}

public interface ListSelectionListener


{ public void valueChanged(ListSelectionEvent e); }

public interface Observer


{ public void update(Observable ob, Object arg); }

public interface Icon


{ public int getIconHeight();
public int getIconWidth();
public void paintIcon(Component c, Graphics g, int x, int y);
}
576

Figure 10.8: frame with label and button


import java.awt.*;
import javax.swing.*;
/** Frame1 is a frame with a label and a button */
public class Frame1 extends JFrame
{ /** Constructor Frame1 creates a frame with a label and button */
public Frame1()
{ JLabel label = new JLabel("Press This:");
JButton button = new JButton("OK");
Container c = getContentPane();
c.setLayout(new FlowLayout());
c.add(label);
c.add(button);
setTitle("Example1");
setSize(200, 60);
setVisible(true);
}

public static void main(String[] args)


{ new Frame1(); }
}

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.

• The message, c.setLayout(new FlowLayout()) tells the content pane to arrange


the components in a flow layout, that is, where the components are arranged in
a line.

• 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"));

Indeed, even the name, c, can be discarded, e.g., getContentPane().setLayout(new


FlowLayout()), getContentPane().add(new JLabel("Press This:")), etc.
The statement, setSize(200, 60), which sets the frame’s size, can be replaced
by pack(), which resizes the frame at a minimal size to display the components it
contains. (But take care when using pack(), because it occasionally creates a frame
with too small of a size for its components.)
As is the custom, we place a tiny main method at the end of the class so that we
can easily test the frame.
As noted earlier, it is possible to paint on the surface of a frame by using a method
named paint. if we add this method
public void paint(Graphics g)
{ g.setColor(Color.red);
g.fillRect(0, 0, 100, 100);
}

to Figure 8, we get this result,

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);

and we can make button a matching yellow by saying,


button.setBackground(Color.yellow);
578

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:

ImageIcon i = new ImageIcon("mypicture.gif");


JButton b = new JButton(i);

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:

JButton b = new JButton("My Picture:", new ImageIcon("mypicture.gif"));

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

10.5 Handling an Event


Every component—button, label, panel, etc.—of a window is an object in its own
right. When an event like a button push or a mouse movement occurs, it occurs
within an object within the window. The object in which the event occurs is the
event source. In AWT/Swing, when an event occurs, the event source automatically
sends a message to an object, called its event listener, to handle the event. The event
listener is a controller—when it receives a message, it sends messages to model object
and view object to compute and display results.
As stated earlier, we focus upon action events—button pushes and menu selec-
tions. An action event, like a button push, is handled by an action-listener object; in
Java, an action-listener object must have a method named actionPerformed, and it
is this method that is invoked when an action event occurs.
When we write a class, C, that creates action-listener objects, we use this format:

public class C implements ActionListener


{ ...

public void actionPerformed(ActionEvent e)


{ ... instructions that handle a button-push event ... }
}

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:

/** ActionListener names the method needed by an action listener. */


public interface ActionListener
{ /** actionPerformed handles an action event, say, a button push
* @param e - information about the event */
public void actionPerformed(ActionEvent e);
}

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.

10.5.1 A View as Action Listener


For our first, simplest example of event handling, we alter Frame1 so that each time
the the frame’s button is pushed, its label displays the total number of button pushes.
580

Figure 10.9: model class, Counter


/** Counter holds a counter */
class Counter
{ private int count; // the count

/** Constructor Counter initializes the counter


* @param start - the starting value for the count */
public Counter(int start)
{ count = start; }

/** increment updates count. */


public void increment()
{ count = count + 1; }

/** countOf accesses count.


* @return the value of count */
public int countOf()
{ return count; }
}

When created, the GUI appears

and after 3 button pushes, the view is

To create this behavior, we use a model-view-controller architecture, where class


Counter, from Figure 9, remembers the quantity of button pushes; it acts as the
model.
Next, we require a controller (action listener) for the OK button, and we must
connect the controller to the button. In this first example, the view and controller
are combined into the same class; this is a naive and inelegant but simple solution—see
Figure 10.
10.5. HANDLING AN EVENT 581

Figure 10.10: combined view/controller for counter example


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/** Frame2a shows a frame with whose label displays the number of times
its button is pushed */
class Frame2a extends JFrame implements ActionListener
{ private Counter count; // address of model object
private JLabel label = new JLabel("count = 0"); // label for the frame

/** Constructor Frame2a creates a frame with a label and button


* @param c - the model object, a counter */
public Frame2a(Counter c)
{ count = c;
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(label);
JButton button = new JButton("OK");
cp.add(button);
button.addActionListener(this); // this object---the view---is connected
// to button as its action listener
setTitle("Frame2a");
setSize(200, 60);
setVisible(true);
}

/** actionPerformed handles an action event---a button push */


public void actionPerformed(ActionEvent e)
{ count.increment();
label.setText("count = " + count.countOf());
}
}

/** Example2a starts the application */


public class Example2a
{ public static void main(String[] args)
{ Counter model = new Counter(0); // create the model
Frame2a view = new Frame2a(model); // create the controller and view
}
}
582

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

4. The program awaits new events.

The explanation should make clear that computation occurs when events trigger exe-
cution of event listeners. This is why computation is “event driven.”

10.5.2 A Separate Controller


Our second approach to the the previous example uses a separate class to be the
button’s event listener—its controller. This style is preferred because it makes it
easier to build GUIs with multiple buttons and to reuse views and controllers. The
controller, class CountController, will be studied momentarily. This leaves the view,
class Frame2b, with the sole job of presenting the frame, label, and button on the
display. Figure 11 shows the view after we have extracted the controller from it.
The key change in the constructor method lies at

JButton button = new JButton("OK");


button.addActionListener(new CountController(count, this));

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

Figure 10.11: view class for counter example


import java.awt.*; import javax.swing.*;
/** Frame2b shows a frame with whose label displays the number of times
its button is pushed */
public class Frame2b extends JFrame
{ private Counter count; // address of model object
private JLabel label = new JLabel("count = 0"); // label for the frame

/** Constructor Frame2b creates a frame with a label and button


* @param c - the model object, a counter */
public Frame2b(Counter c)
{ count = c;
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(label);
JButton button = new JButton("OK");
button.addActionListener(new CountController(count, this)); // see Fig. 12
cp.add(button);
setTitle("Frame2");
setSize(200, 60);
setVisible(true);
}

/** update revises the view */


public void update()
{ label.setText("count = " + count.countOf()); }
}

/** Example2b starts the application */


public class Example2b
{ public static void main(String[] args)
{ Counter model = new Counter(0);
Frame2b view = new Frame2b(model);
}
}
10.5. HANDLING AN EVENT 585

Figure 10.12: controller for counter example


import java.awt.event.*;
/** CountController handles button push events that increment a counter */
public class CountController implements ActionListener
{ private Frame2b view; // the view that must be refreshed
private Counter model; // the counter model

/** CountController constructs the controller


* @param my model - the model object
* @param my view - the view object */
public CountController(Counter my model, Frame2b my view)
{ view = my view;
model = my model;
}

/** actionPerformed handles a button-push event */


public void actionPerformed(ActionEvent evt)
{ model.increment();
view.update();
}
}

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

Figure 10.13: button-controller for counter example


import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/** CountButton defines a button-controller */
public class CountButton extends JButton implements ActionListener
{ private Frame2c view; // the view that holds this controller
private Counter model; // the model that this controller collaborates with

/** Constructor CountButton builds the controller


* @param my label - the label on the button that represents the controller
* @param my model - the model that the controller collaborates with
* @param my view - the view that the controller updates */
public CountButton(String my label, Counter my model, Frame2c my view)
{ super(my label); // attach label to the button in the superclass
view = my view;
model = my model;
addActionListener(this); // attach this very object as the ‘‘listener’’
}

/** actionPerformed handles a push of this button


* @param evt - the event that occurred, namely, the button push */
public void actionPerformed(ActionEvent evt)
{ model.increment();
view.update();
}
}
10.5. HANDLING AN EVENT 587

Figure 10.14: view for Example2


import java.awt.*;
import javax.swing.*;
/** Frame2c shows a frame with whose label displays the number of times
its button is pushed */
public class Frame2c extends JFrame
{ private Counter count; // address of model object
private JLabel label = new JLabel("count = 0"); // label for the frame

/** Constructor Frame2c creates a frame with a label and button


* @param c - the model object, a counter */
public Frame2c(Counter c)
{ count = c;
Container cp = getContentPane();
cp.setLayout(new FlowLayout());
cp.add(label);
cp.add(new CountButton("OK", count, this)); // the button-controller
setTitle("Example2");
setSize(200, 60);
setVisible(true);
}

/** update revises the view */


public void update()
{ label.setText("count = " + count.countOf()); }
}

/** Example2c starts the application */


public class Example2c
{ public static void main(String[] args)
{ Counter model = new Counter(0);
Frame2c view = new Frame2c(model);
}
}
588

Figure 10.15: architecture of Example2c

AWT/Swing classes
JButton ActionListener that detect events
actionPerformed(ActionEvent e)

CountButton Frame2c JFrame


actionPerformed(ActionEvent e) update()

Counter
increment()
countOf()

Figure 10.16: exit controller


import javax.swing.*;
import java.awt.event.*;
/** ExitButton defines a controller that terminates an application */
public class ExitButton extends JButton implements ActionListener
{
/** Constructor ExitButton builds the controller
* @param my label - the label for the controller’s button */
public ExitButton(String my label)
{ super(my label);
addActionListener(this);
}

/** actionPerformed handles a button-push event


* @param evt - the event */
public void actionPerformed(ActionEvent evt)
{ System.exit(0); }
}
10.5. HANDLING AN EVENT 589

Figure 10.17: view for two-button view


import java.awt.*;
import javax.swing.*;
/** Frame3 shows a frame with whose label displays the number of times
its button is pushed */
class Frame3 extends Frame2c
{
/** Constructor Frame3 creates a frame with a label and button
* @param c - the model object, a counter */
public Frame3(Counter c)
{ super(c); // tell superclass to construct most of the frame
Container cp = getContentPane();
cp.add(new ExitButton("Exit")); // add another button-controller
setTitle("Example 3"); // reset the correct title and size:
setSize(250, 60);
setVisible(true);
}
}

/** Example3 starts the application */


public class Example3
{ public static void main(String[] args)
{ Counter model = new Counter(0);
Frame3 view = new Frame3(model);
}
}

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

Figure 10.18: terminating a program with the X-button


// Define this class:
import java.awt.event.*;
public class ExitController extends WindowAdapter
{ public void windowClosing(WindowEvent e)
{ System.exit(0); }
}

// Within the frame’s constructor method, add this statement:


addWindowListener(new ExitController());

2. As noted in Figure 5, buttons have a setText(String s) method, which changes


the text that appears on a button’s face to s. Revise your solution to the
previous exercise so that each button displays the number of times that it has
been pushed.

3. Create an application whose GUI has an Increment button, a Decrement button,


and label that displays an integer. Each time Increment is pushed, the integer
increases, and each time Decrement is pushed, the integer decreases. (Hint:
Write a new model class.)

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.

10.6 Richer Layout: Panels and Borders


We can rebuild the above GUI so that its label appears at the top of the frame,
the OK and Exit buttons are positioned along the frame’s bottom, and a drawing of
the current count appears in the center. Figure 19 shows this. To do this, we use
panels. We construct a panel, insert the label in it, and place the panel in the “north”
region of the frame. Next, we construct another panel, paint it, and place it in the
frame’s “center” region. Finally, we insert the two buttons into a third panel and
place the panel in the “south” region. Border layout lets us specify the regions where
components can be inserted; in addition to the region used in the example in Figure
19, there are also “east” and “west” regions. Here is a picture of how the regions are
10.6. RICHER LAYOUT: PANELS AND BORDERS 591

Figure 10.19: frame with two depictions of the count

positioned in a container by the border layout manager:

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

Figure 10.20: view class with border layout and panels


import java.awt.*;
import javax.swing.*;
/** Frame4 is a frame with a label and a button */
public class Frame4 extends JFrame
{ private Counter count; // address of model object
private JLabel lab = new JLabel("count = 0"); // label for the frame
private JPanel drawing; // a drawing for the center of the frame

/** 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);
}

/** update revises the view */


public void update()
{ lab.setText("count = " + count.countOf());
drawing.repaint();
}
}
10.6. RICHER LAYOUT: PANELS AND BORDERS 593

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

public Drawing(Counter model)


{ count = model;
setSize(200, 80);
}

public void paintComponent(Graphics g)


{ g.setColor(Color.white);
g.fillRect(0, 0, 150, 80);
g.setColor(Color.red);
for ( int i = 0; i != count.countOf(); i = i+1 )
{ g.fillOval(i * 25, 0, 20, 20); }
}
}

/** Example4 starts the application */


public class Example4
{ public static void main(String[] args)
{ Counter model = new Counter(0);
Drawing drawing = new Drawing(model);
Frame4 view = new Frame4(model, drawing);
}
}

JPanel p2 = new JPanel(new FlowLayout());


p2.add(new CountButton("Count", count, this));
p2.add(new ExitButton("Quit"));
cp.add(p2, BorderLayout.SOUTH);

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

g.drawImage(j, 20, 20, this);

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.

10.6.1 An Animation in a Panel

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

Figure 10.21: architecture of throbbing ball animation

ColorButton ThrobPanel ThrobFrame

ThrobController ThrobbingBall

“throbs” between large and small:

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

Figure 10.22: model and view classes for animation


/** ThrobbingBall models a ball that changes size from large to small */
public class ThrobbingBall
{ private boolean is it currently large; // the ball’s state---large or small

public ThrobbingBall() { is it currently large = true; }

/** isLarge returns the current state of the ball */


public boolean isLarge() { return is it currently large; }

/** 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

public ThrobPanel(int size, ThrobbingBall b)


{ panel size = size;
location = panel size / 2;
ball size = panel size / 3;
ball = b;
setSize(panel size, panel size);
}

/** getColor returns the current color of the ball */


public Color getColor() { return c; }

/** setColor resets the color of the ball to new color */


public void setColor(Color new color) { c = new color; }

...
10.6. RICHER LAYOUT: PANELS AND BORDERS 597

Figure 10.22: model and view classes for animation (concl.)


/** paintComponent paints the ball */
public void paintComponent(Graphics g)
{ g.setColor(Color.white);
g.fillRect(0, 0, panel size, panel size);
g.setColor(c);
if ( ball.isLarge() )
{ g.fillOval(location, location, ball size, ball size); }
else { g.fillOval(location, location, ball size / 2, ball size / 2); }
}
}

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

Figure 10.23: Controllers for animation


/** ThrobController runs the throbbing-ball animation */
public class ThrobController
{ private ThrobPanel writer; // the output-view panel
private ThrobbingBall ball; // the ball model object
private int time; // how long animation is delayed before redrawn

/** ThrobController initializes the controller


* @param w - the panel that is controlled
* @param b - the ball that is controlled
* @param delay time - the amount of time between redrawing the animation */
public ThrobController(ThrobPanel w, ThrobbingBall b, int delay time)
{ writer = w;
ball = b;
time = delay time;
}

/** run runs the animation forever */


public void run()
{ while ( true )
{ ball.throb();
writer.repaint(); // redisplay ball
delay();
}
}

/** delay pauses execution for time milliseconds */


private void delay()
{ try { Thread.sleep(time); }
catch (InterruptedException e) { }
}
}
10.6. RICHER LAYOUT: PANELS AND BORDERS 599

Figure 10.23: Controllers for animation (concl.)


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/** ColorButton controls the color of the ball */
public class ColorButton extends JButton implements ActionListener
{ private ThrobPanel view; // the view object where shapes are drawn

public ColorButton(ThrobPanel f)
{ super("OK");
view = f;
addActionListener(this);
}

/** actionPerformed handles a click */


public void actionPerformed(ActionEvent e)
{ Color c = view.getColor();
if ( c == Color.red )
{ view.setColor(Color.blue); }
else { view.setColor(Color.red); }
}
}

/** StartThrob assembles the objects of the animation */


public class StartThrob
{ public static void main(String[] a)
{ int frame size = 180; // size of displayed frame
int pause time = 200; // speed of animation (smaller is faster)
ThrobbingBall b = new ThrobbingBall();
ThrobPanel p = new ThrobPanel(frame size, b);
ThrobFrame f = new ThrobFrame(frame size, p, new ColorButton(p));
new ThrobController(p, b, pause time).run(); // important: do this last!
}
}
600

“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

1. Add a “Pause” button to the throbbing-ball animation that, when pushed,


causes the ball to stop throbbing until the button is pushed again. (Hint: add
another boolean field variable to ThrobbingBall.)

2. Swap the last two statements of main in StartThrob. Explain what the anima-
tion no longer operates.

3. Embed the moving-ball animation of Figure 7, Chapter 7 into a panel; insert


the panel into a frame. Next, add two buttons, “Faster” and “Slower,” which
increase and decrease, respectively, the velocity at which the ball travels in the
animation. (You will have to write additional methods for class MovingBall in
Figure 8, Chapter 7; the methods will adjust the ball’s x- and y-velocities.)

10.7 Grid Layout

When you have a collection of equally-sized components to be arranged as a table or


grid, use grid layout. As an example, consider the slide puzzle program from Figure
11, Chapter 8, where its output view, class PuzzleWriter, is replaced by a GUI
where buttons portray the pieces of the puzzle—a click on a button/piece moves the
10.7. GRID LAYOUT 601

puzzle piece. The view might look like this:

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

Figure 10.24: grid layout for slide puzzle game


import java.awt.*; import javax.swing.*;
/** PuzzleFrame shows a slide puzzle */
public class PuzzleFrame extends JFrame
{ private SlidePuzzleBoard board; // the model; see Fig. 10, Ch. 8
private int size; // the board’s size
private int button size = 60; // width/height of each button
private PuzzleButton[][] button; // the buttons on the face of the view

/** Constructor PuzzleFrame builds the view


* @param board size - the width and depth of the puzzle
* @param b - the model, a slide puzzle board */
public PuzzleFrame(int board size, SlidePuzzleBoard b)
{ size = board size;
board = b;
button = new PuzzleButton[size][size];
Container cp = getContentPane();
cp.setLayout(new GridLayout(size, size));
// create the button-controllers and insert them into the layout:
for ( int i = 0; i != size; i = i+1 )
{ for ( int j = 0; j != size; j = j+1 )
{ button[i][j] = new PuzzleButton(board, this);
cp.add(button[i][j]);
}
}
update(); // initialize the pieces with their numbers
addWindowListener(new ExitController()); // activates X-button; see Fig. 15
setTitle("PuzzleFrame");
setSize(size * button size + 10, size * button size + 20);
setVisible(true);
}

/** update consults the model and repaints each button */


public void update()
{ PuzzlePiece[][] r = board.contents(); // get contents of the puzzle
for ( int i = 0; i != size; i = i+1 ) // redraw the faces of the buttons
{ for ( int j = 0; j != size; j = j+1 )
{ if ( r[i][j] != null )
{ button[i][j].setBackground(Color.white);
button[i][j].setText("" + r[i][j].valueOf()); }
else { button[i][j].setBackground(Color.black);
button[i][j].setText( "" );
}
}
}
}
}
10.7. GRID LAYOUT 603

Figure 10.24: grid layout for slide puzzle game (concl.)


/** Puzzle creates and displays the slide puzzle */
public class Puzzle
{ public static void main(String[] args)
{ int size = 4; // a 4 x 4 slide puzzle
SlidePuzzleBoard board = new SlidePuzzleBoard(size); // see Fig. 10, Ch. 8
PuzzleFrame frame = new PuzzleFrame(size, board);
}
}

Figure 10.25: button-controller for slide puzzle


import javax.swing.*;
import java.awt.event.*;
/** PuzzleButton implements a button controller for a puzzle game */
public class PuzzleButton extends JButton implements ActionListener
{ private SlidePuzzleBoard puzzle; // address of the SlidePuzzle model
private PuzzleFrame view; // address of Frame that displays this button

/** Constructor PuzzleButton builds the button


* @param my puzzle - the address of the puzzle model object
* @param my view - the address of the puzzle’s view */
public PuzzleButton(SlidePuzzleBoard my puzzle, PuzzleFrame my view)
{ super(""); // set label to nothing, but this will be repainted by the view
puzzle = my puzzle;
view = my view;
addActionListener(this);
}

/** actionPerformed processes a move of the slide puzzle */


public void actionPerformed(ActionEvent evt)
{ String s = getText(); // get the number on the face of this button
if ( !s.equals("") ) // it’s not the blank space, is it?
{ boolean ok = puzzle.move(new Integer(s).intValue()); // try to move
if ( ok ) { view.update(); }
}
}
}
604

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.

10.8 Scrolling Lists


A scrolling list (or “list”, for short) can be used when a user interface must display
many items of information. The scrolling list has the added advantage that a user can
click on one or more of its items, thus highlighting or selecting them. (Selecting a list
item generates an event—not an action event, but a list selection event. The default
event listener for a list selection event highlights the selected item.) At a subsequent
button push, the list’s items can be examined and altered.
Here is a small example that uses a scrolling list. The list displays the values of
eight counters. When a user clicks on a list item, it is highlighted. Here, the third
item is selected:
10.8. SCROLLING LISTS 605

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:

String[] list_labels = new String[how_many]; // the list’s ‘‘model’’


JList items = new JList(list_labels); // embed model into the list
JScrollPane sp = new JScrollPane(items); // embed list into a scroll pane

Now, sp can be added into a content pane, e.g.,


Container cp = getContentPane();
...
cp.add(sp);

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"; }

At the beginning of the chapter, we noted that each AWT/Swing component is


built as a little MVC-architecture of its own. When we create a JList object, we must
supply its model part, which must be an array of objects, e.g., strings like list labels
just seen. If the objects are not strings, then we must ensure that the objects have
606

Figure 10.26: model and view for scrolling list of counters


/** ListExample displays an array of counters as a scrolling list */
public class ListExample
{ public static void main(String[] a)
{ int how many counters = 8;
Counter2[] counters = new Counter2[how many counters]; // the model
for ( int i = 0; i != how many counters; i = i+1 )
{ counters[i] = new Counter2(0, i); } // see below
new ListFrame(counters); // the view
}
}

/** Counter2 is a Counter that states its identity with a toString method */
public class Counter2 extends Counter
{ private int my index;

public Counter2(int start, int index)


{ super(start);
my index = index;
}

public String toString()


{ return "Counter " + my index + " has " + countOf(); }
}

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 extend class Counter in Figure 9 to class Counter2 by writing a toString


method for it.

• We use as the application’s model, a Counter2[] object, that is, an array of


counters.

• 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

/** Constructor ListFrame generates the frame with the list


* @param model - the model object that will be displayed as a list */
public ListFrame(Counter2[] model)
{ counters = model;
items = new JList(counters); // embed the model into a JList
JScrollPane sp = new JScrollPane(items); // attach a scroll bar
Container cp = getContentPane();
cp.setLayout(new GridLayout(2,1));
cp.add(sp); // add the scrolling list to the pane
JPanel p = new JPanel(new GridLayout(2,1));
p.add(new ListButton("Go", counters, this)); // see Figure 27
p.add(new ExitButton("Quit")); // see Figure 16
cp.add(p);
update(); // initialize the view of the list
setTitle("ListExample");
setSize(200,200);
setVisible(true);
}

/** getSelection returns which list item is selected by the user


* @return the element’s index, or -1 is no item is selected */
public int getSelection()
{ return items.getSelectedIndex(); }

/** update refreshes the appearance of the list */


public void update()
{ items.clearSelection(); } // deselect the selected item in the list
}
608

Figure 10.27: button-controller for scrolling list example


import java.awt.event.*;
import javax.swing.*;
/** ListButton implements a button that alters a scrolling list */
public class ListButton extends JButton implements ActionListener
{ private Counter2[] counters; // address of model object
private ListFrame view; // address of view object

/** Constructor ListButton constructs the controller */


public ListButton(String label, Counter2[] c, ListFrame v)
{ super(label);
counters = c;
view = v;
addActionListener(this);
}

/** actionPerformed handles a button-push event */


public void actionPerformed(ActionEvent evt)
{ int choice = view.getSelection(); // get selected index number
if ( choice != -1 ) // Note: -1 means no item was selected.
{ counters[choice].increment();
view.update();
}
}
}

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

ListSelectionListener interface in Figure 7. For example, we can remove the Go


button from the frame in Figure 26, delete Figure 27 altogether, and replace the
latter with this controller:

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

/** Constructor ListController constructs the controller */


public ListController(Counter2[] c, ListFrame v)
{ counters = c;
view = v;
}

/** valueChanged responds to a list item selection */


public void valueChanged(ListSelectionEvent e)
{ int choice = view.getSelection(); // get selected index number
if ( choice != -1 )
{ counters[choice].increment();
view.update();
}
}
}

Then, within the constructor method of class ListFrame, we attach the controller as
a listener to the JList items:

items.addListSelectionListener(new ListController(counters, this));

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()

which returns as its answer an array of integers.


610

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.

10.9 Text Fields


AWT/Swing provides a JTextField component, which lets a user type one line of
text into a text field. An example text field appears in Figure 2 at the start of this
Chapter. Typing text into a text field generates events (but not action events) that
are processed by the default event listener for a text field; the event listener displays
the typed text in the text field, and it will accommodate backspacing and cursor
movement. One problem that arises with text fields is that a program’s user might
type something inappropriate into the text field, so the program must be prepared to
issue error messages in response.
Creating and adding a text field to a frame is easy, e.g.,
JTextField input_text = new JTextField("0", 8);
Container cp = getContentPane();
...
cp.add(input_text);

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

Figure 10.28: view class for temperature converter


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/** AbsTempFrame creates a view that displays temperature conversions */
public abstract class AbsTempFrame extends JFrame
{ private String START TEXT = "0";
private String BLANKS = " ";

private JTextField input text = new JTextField(START TEXT, 8);


private JLabel answer = new JLabel(BLANKS);
// components for the temperature scales list:
private String[] choices = {"Celsius", "Fahrenheit"};
private JList scales = new JList(choices);

/** AbsTempFrame constructs the frame */


public AbsTempFrame()
{ // the controller that triggers temperature conversion; see Figure 29:
ComputeTempButton compute controller = new ComputeTempButton("Go", this);
Container cp = getContentPane();
cp.setLayout(new GridLayout(4, 1));
JPanel p1 = new JPanel(new FlowLayout());
p1.add(new JLabel("Convert degrees:"));
cp.add(p1);
JPanel p2 = new JPanel(new FlowLayout());
p2.add(input text);
p2.add(scales);
cp.add(p2);
JPanel p3 = new JPanel(new FlowLayout());
p3.add(answer);
cp.add(p3);
JPanel p4 = new JPanel(new FlowLayout());
p4.add(compute controller);
p4.add(new ResetButton("Reset", this)); // see Figure 29
p4.add(new ExitButton("Bye")); // see Figure 16
cp.add(p4);
resetFields(); // initialize the view
setSize(240, 180);
setTitle("Temperature Convertor");
setVisible(true);
}
...
10.9. TEXT FIELDS 613

Figure 10.28: view class for temperature converter (concl.)


/** getInputs returns the inputs the user typed and selected.
* @return (1) the string the user typed, and
* (2) "Celsius" or "Fahrenheit" */
public String[] getInputs()
{ String[] input = new String[2];
input[0] = input text.getText();
input[1] = choices[scales.getSelectedIndex()];
return input;
}

/** displayAnswer resets the label


* @param s - the string used to reset the label */
public void displayAnswer(String s)
{ answer.setText(s); }

/** displayError displays an error message


* @param s - the message */
public abstract void displayError(String s); // will be coded later

/** resetFields resets the view’s text field */


public void resetFields()
{ input text.setText(START TEXT);
answer.setText(BLANKS);
scales.setSelectedIndex(0); // reset scale selection
}
}

/** TempFrame builds a completed view for a temperature converter */


public class TempFrame extends AbsTempFrame
{
public TempFrame()
{ super(); }

public void displayError(String s)


{ displayAnswer("Error: " + s); } // invoke method in superclass
}
614

Figure 10.29: controllers and model for temperature conversion


import javax.swing.*;
import java.awt.event.*;
/** ComputeTempButton implements a button that converts temperatures */
public class ComputeTempButton extends JButton implements ActionListener
{ private TempCalculator calc = new TempCalculator();
// the model object for calculating temperatures; see Fig. 5, Ch. 6
private AbsTempFrame view; // address of the view object

/** Constructor ComputeTempButton constructs the button


* @param v - the address of the view object */
public ComputeTempButton(String label, AbsTempFrame v)
{ super("Go");
view = v;
addActionListener(this);
}

/** actionPerformed calculates the temperature */


public void actionPerformed(ActionEvent evt)
{ try { String[] s = view.getInputs(); // get temp and scale
double start temp = new Double(s[0].trim()).doubleValue();
String answer = "is ";
if ( s[1].equals("Celsius") )
{ answer = answer + calc.celsiusIntoFahrenheit(start temp)
+ " Fahrenheit"; }
else { answer = answer + calc.fahrenheitIntoCelsius(start temp)
+ " Celsius"; }
view.displayAnswer(answer);
}
catch(RuntimeException e) // if s[0] is nonnumeric, an exception occurs
{ view.displayError(e.getMessage()); }
}
}
10.9. TEXT FIELDS 615

Figure 10.29: controllers and model for temperature conversion (concl.)


import javax.swing.*;
import java.awt.event.*;
/** ResetButton resets the fields of a lottery GUI */
public class ResetButton extends JButton implements ActionListener
{ private AbsTempFrame view; // address of the view object that gets reset

/** Constructor ResetButton constructs the button


* @param v - the address of the view object */
public ResetButton(String label, AbsTempFrame v)
{ super(label);
view = v;
addActionListener(this);
}

/** actionPerformed resets the view’s text fields */


public void actionPerformed(ActionEvent evt)
{ view.resetFields(); }
}

/** TempCalculator models conversion between Celsius and Fahrenheit */


public class TempCalculator
{ /** celsiusIntoFahrenheit translates degrees Celsius into Fahrenheit
* @param c - the degrees in Celsius
* @return the equivalent degrees in Fahrenheit */
public double celsiusIntoFahrenheit(double c)
{ return ((9.0/5.0) * c) + 32; }

/** fahrenheitIntoCelsius translates degrees Fahrenheit into Celsius


* @param f - the degrees in Fahrenheit
* @return the equivalent degrees in Celsius */
public double fahrenheitIntoCelsius(double f)
{ return (f - 32) * (5.0/9.0); }
}
616

Figure 10.30: architecture of temperature converter

AWT/Swing classes that detect events

ResetButton AbsTempFrame
actionPerformed getInputs JFrame
ActionListener displayAnswer
actionPerformed displayError
ComputeTempButton resetFields
actionPerformed

TemperatureCalculator TempFrame
celsiusIntoFahrenheit displayError
fahrenheitIntoCelsius

double start_temp = new Double(s[0].trim()).doubleValue();

cannot complete—it generates a RuntimeException. As we learned in Chapter 3, the


exception can be caught by the catch clause of the exception handler,

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()); }

and the statement, view.displayError(e.getMessage()) executes. This sends a displayError


message to the view object, where the parameter, e.getMessage(), computes to a
string that describes the error.
Figure 30 surveys the architecture of the temperature converter. A startup class
is needed to create objects from the classes in the Figure. The startup class can be
just this:

public class Temp


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

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:

private JTextField input_text = new JTextField(START_TEXT, 8);


10.10. ERROR REPORTING WITH DIALOGS 617

and the controller that responds to user input was defined as

ComputeTempButton compute_controller = new ComputeTempButton("Go", this);

Now, we merely add this statement to the constructor in Figure 28:

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.)

10.10 Error Reporting with Dialogs


A graphical user interface usually reports an error by constructing a dialog that
displays an error message. A dialog is meant to halt an application’s execution and
alert its user to a situation that demands immediate attention. After the user responds
to the dialog—typically, by pushing one of its buttons—the dialog disappears and the
application resumes execution.
618

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,

JOptionPane.showMessageDialog(owner, "Some information for you to read");

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:

public class Temp2


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

A second form of dialog is the confirm dialog, which displays a message and asks
10.10. ERROR REPORTING WITH DIALOGS 619

Figure 10.31: frame that generates a message dialog


import java.awt.*;
import javax.swing.*;
/** TempFrame2 builds a complete view for a temperature converter that
* displays a message dialog in case of an error. */
public class TempFrame2 extends AbsTempFrame // see Figure 28
{ public TempFrame2()
{ super(); }

public void displayError(String s)


{ JOptionPane.showMessageDialog(this, "Error in input: " + s); }
}

the user for a decision:

The dialog is generated by a statement like this one:

int i = JOptionPane.showConfirmDialog(owner, "Please choose one:");

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"); }

The possible values returned are JOptionPane.YES OPTION, JOptionPane.NO OPTION,


JOptionPane.CANCEL OPTION, and JOptionPane.CLOSED OPTION, the last arising when
the user pushes the “X”-button to terminate the dialog.
The third form of dialog is an input dialog, which lets the user type text before
620

dismissing the dialog:

This dialog is created by


String s = JOptionPane.showInputDialog(owner, "Please type a string:");

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.

2. Improve the arithmetic calculator from Exercise 2 of the previous section so


that a message dialog is displayed if the user types a nonnumber into one of the
calculator’s text fields.

10.11 TextAreas and Menus


A text area is a text component into which multiple lines of text can be typed; an
example of one appears in Figure 4 at the start of the Chapter—the large white area
in the frame is the text area. Like buttons, lists, and text fields, a text area has its
own little MVC-architecture, where the model part holds the lines of text that the
user types into the text area, and the view part displays the model’s contents in the
10.11. TEXTAREAS AND MENUS 621

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

Figure 10.32: methods for text areas

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.

class JTextArea extends


JTextComponent
Methods
setFont(Font f) Set the font used to display the text to f. A typical
value for f is new Font("Courier", Font.PLAIN,
14).
setLineWrap(boolean b) State whether or not a line longer than the width of
the text area will be displayed completely by “wrap-
ping” it to the next line.
insert(String s, int i) Insert string s at position i in the text area.
replaceRange(String s, int Replace the string within the text area starting
start, int end) at position start and ending at position end-1 by
string s.
10.11. TEXTAREAS AND MENUS 623

JMenuBar mbar = new JMenuBar();


JMenu file = new JMenu("File"); // the "File" menu
... // statements go here that add menu items to the File menu
mbar.add(file); // attach menu to menu bar
JMenu edit = new JMenu("Edit"); // the "Edit" menu
// add these menu items to the Edit menu:
edit.add(new JMenuItem("Cut"));
edit.add(new JMenuItem("Copy"));
edit.add(new JMenuItem("Paste"));
edit.addSeparator(); // adds a separator bar to the menu
JMenu search = new JMenu("Search");
... // statements go here that add menu items to the Search menu
edit.add(search); // a menu can be added to a menu
mbar.add(edit);
setJMenuBar(mbar); // attach menu bar to frame

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.

10.11.1 Case Study: Text Editor


We put menus and text areas to work in an interactive text editor. Figure 4 displays
the view of the editor we will build. The editor uses two menus, each of which
contains several menu items. A user selects a menu item by clicking the mouse on
the menu name, dragging the mouse to the desired item, and releasing the mouse.
This generates an action event, like a button push, that can be handled with an
actionPerformed method. Building menu items, action listeners, and menus will be
straightforward.
A first draft of the text editor’s architecture appears in Figure 33. The editor’s
model will be an EditModel that extends a JTextArea. We do this because the editor’s
model is just the text contained in the text area—the text area becomes the model. A
variety of menu items (controllers) will consult the model and update it. The editor’s
view will be presented by EditFrame, which displays the menus and text area.
When selected, the ReplaceMenuItem will display a secondary frame, called ReplaceFrame,
that helps a user find and replace a string in the text area. Figure 34 shows the
ReplaceFrame that appears. Unlike a dialog, the ReplaceFrame is created when the
application starts; it appears and disappears as directed by the user. (Pressing its
Close button makes the frame disappear.) Unlike a dialog, the frame can be open
and operating at the same time the EditFrame is also open and operating.
The construction of the view, class EditFrame, is portrayed in Figure 35. It is
straightforward. The classes for the various menu items and ReplaceFrame will be
624

Figure 10.33: partial class diagram of text editor

ActionListener AWT/Swing classes that detect events

ClearMenuItem
QuitMenuItem EditModel JTextArea
CutMenuItem
CopyMenuItem
PasteMenuItem EditFrame
FindMenuItem
JFrame
ReplaceMenuItem ReplaceFrame

Figure 10.34: editor with auxiliary frame


10.11. TEXTAREAS AND MENUS 625

Figure 10.35: view for text editor


import java.awt.*;
import javax.swing.*;
/** EditFrame displays a text editor with two menus and a text area. */
public class EditFrame extends JFrame
{ // the EditModel, a subclass of JTextArea, is the ‘‘model’’:
private EditModel buffer = new EditModel("", 15, 50);

/** Constructor EditFrame builds the editor interface */


public EditFrame()
{ // Create the ReplaceFrame, which appears when the user selects ‘‘Replace’’:
ReplaceFrame second frame = new ReplaceFrame(buffer);
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JMenuBar mbar = new JMenuBar();
JMenu file = new JMenu("File"); // defines the "File" menu
file.add(new ClearMenuItem("New", buffer));
file.add(new QuitMenuItem("Exit"));
mbar.add(file); // attach menu to menu bar
JMenu edit = new JMenu("Edit"); // defines the "Edit" menu
edit.add(new CutMenuItem("Cut", buffer));
edit.add(new CopyMenuItem("Copy", buffer));
edit.add(new PasteMenuItem("Paste", buffer));
edit.addSeparator();
JMenu search = new JMenu("Search"); // defines the "Search" submenu
search.add(new FindMenuItem("Find", buffer));
search.add(new ReplaceMenuItem("Replace", second frame));
edit.add(search);
mbar.add(edit);
setJMenuBar(mbar); // attach menu bar to frame
JScrollPane sp = new JScrollPane(buffer); // embed into a scroll pane
cp.add(sp, BorderLayout.CENTER);
setTitle("EditFrame");
pack();
setVisible(true);
}
}
626

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

Figure 10.36: text area for editor


import java.awt.*; import javax.swing.*;
/** EditModel models a text area */
public class EditModel extends JTextArea
{ /** EditModel builds the text area
* @param initial text - the starting text for the text area
* @param rows - the number of rows
* @param cols - the number of columns */
public EditModel(String initial text, int rows, int cols)
{ super(initial text, rows, cols); // create the underlying JTextArea
setLineWrap(true);
setFont(new Font("Courier", Font.PLAIN, 14));
}

/** clear resets the text area to be empty */


public void clear()
{ setText(""); }

/** 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); }

/** findFromCaret locates a string starting from the caret position


* @param s - the string to be found
* @return the position where s is first found; -1, if s not found */
public int findFromCaret(String s)
{ return find(s, getCaretPosition()); }
}
628

Figure 10.37: menu item-controllers for text editor


import javax.swing.*; import java.awt.event.*;
/** QuitMenuItem terminates the text editor. */
public class QuitMenuItem extends JMenuItem implements ActionListener
{ public QuitMenuItem(String label)
{ super(label);
addActionListener(this);
}

public void actionPerformed(ActionEvent e)


{ System.exit(0); }
}

import javax.swing.*; import java.awt.event.*;


/** EditorMenuItem defines a generic menu item for the text editor */
public abstract class EditorMenuItem extends JMenuItem implements ActionListener
{ private EditModel buffer; // address of the model manipulated by the menu item

public EditorMenuItem(String label, EditModel model)


{ super(label);
buffer = model;
addActionListener(this);
}

/** myModel returns the address of the model this menu item manipulates */
public EditModel myModel()
{ return buffer; }

public abstract void actionPerformed(ActionEvent e);


}

import java.awt.event.*;
/** ClearMenuItem clears a text area */
public class ClearMenuItem extends EditorMenuItem
{ public ClearMenuItem(String label, EditModel model)
{ super(label, model); }

public void actionPerformed(ActionEvent e)


{ myModel().clear(); }
}
10.11. TEXTAREAS AND MENUS 629

Figure 10.37: menu item-controllers for text editor (cont.)


import java.awt.event.*;
/** CutMenuItem cuts the selected text from the text area. */
public class CutMenuItem extends EditorMenuItem
{ public CutMenuItem(String label, EditModel model)
{ super(label, model); }

public void actionPerformed(ActionEvent e)


{ myModel().cut(); }
}

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); }

public void actionPerformed(ActionEvent e)


{ myModel().copy(); }
}

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); }

public void actionPerformed(ActionEvent e)


{ myModel().paste(); }
}
630

Figure 10.37: menu item-controllers for text editor (concl.)


import javax.swing.*; import java.awt.event.*;
/** FindMenuItem generates a dialog to find a string in the text area */
public class FindMenuItem extends EditorMenuItem
{ public FindMenuItem(String label, EditModel model)
{ super(label, model); }

public void actionPerformed(ActionEvent e)


{ String s = JOptionPane.showInputDialog(this, "Type string to be found:");
if ( s != null )
{ int index = myModel().findFromCaret(s);
if ( index == -1 )
{ int response = JOptionPane.showConfirmDialog(this,
"String " + s + " not found. Restart search from beginning of buffer?");
if ( response == JOptionPane.YES OPTION )
{ index = myModel().findFromStart(s);
if ( index == -1 )
{ JOptionPane.showMessageDialog(this,
"String " + s + " not found");
}
}
}
}
}
}

import javax.swing.*; import java.awt.event.*;


/** ReplaceMenuItem shows the frame that helps the user replace strings */
public class ReplaceMenuItem extends JMenuItem implements ActionListener
{ private ReplaceFrame my view;

public ReplaceMenuItem(String label, ReplaceFrame view)


{ super(label);
my view = view;
addActionListener(this);
}

public void actionPerformed(ActionEvent e)


{ my view.setVisible(true); }
}
10.11. TEXTAREAS AND MENUS 631

Figure 10.38: frame that replaces strings


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/** ReplaceFrame shows a frame that helps a user find and replace a string */
public class ReplaceFrame extends JFrame implements ActionListener
{ private EditModel model;
private JButton replace = new JButton("Replace");
private JButton clear = new JButton("Clear");
private JButton close = new JButton("Close");
private JTextField find text = new JTextField("", 20);
private JTextField replace text = new JTextField("", 20);

public ReplaceFrame(EditModel my model)


{ model = my model;
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p1 = new JPanel(new GridLayout(2, 1));
JPanel p11 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p11.add(new JLabel("From caret, replace "));
p11.add(find text);
p1.add(p11);
JPanel p12 = new JPanel(new FlowLayout(FlowLayout.RIGHT));
p12.add(new JLabel("by "));
p12.add(replace text);
p1.add(p12);
cp.add(p1, BorderLayout.CENTER);
JPanel p2 = new JPanel(new FlowLayout());
p2.add(replace);
p2.add(clear);
p2.add(close);
cp.add(p2, BorderLayout.SOUTH);
replace.addActionListener(this);
clear.addActionListener(this);
close.addActionListener(this);
setTitle("ReplaceFrame");
pack();
setVisible(false);
}
632

Figure 10.38: frame that replaces strings (concl.)


/** actionPerformed handles all button pushes on this frame
* @param e - contains the identity of the button that is pushed */
public void actionPerformed(ActionEvent e)
{ if ( e.getSource() == close ) // was it the Close button?
{ setVisible(false); }
else if ( e.getSource() == clear ) // the Clear button?
{ find text.setText("");
replace text.setText("");
}
else if ( e.getSource() == replace ) // the Replace button?
{ String find = find text.getText();
int location = model.findFromCaret(find);
if ( location == -1 ) // string not found?
{ JOptionPane.showMessageDialog(this,
"String " + find + " not found");
}
else { model.replaceRange(replace text.getText(),
location, location + find.length());
}
}
}
}

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.

10.12 Event-Driven Programming with Observers


We saw in this Chapter how events, triggered by button pushes and menu-item selects,
direct the execution of a program. We can write a program that triggers its own
events internally by using the Java class Observer and interface Observable from
the java.util package. This lends itself to an event-driven programming style that
further “decouples” components from one another.
First, we must learn how event listeners are programmed into graphical compo-
nents in AWT/Swing. Every event-generating graphical component (e.g., a button)
has, as part of its internal model, an array of addresses of its listener objects. A listener
object, ob, is added to button b’s array by the message, b.addActionListener(ob).
10.12. EVENT-DRIVEN PROGRAMMING WITH OBSERVERS 633

Figure 10.39: a counter that generates its own events


import java.util.*;
/** Counter3 holds a counter that can be observed by Observers */
public class Counter3 extends Observable
{ private int count; // the count

/** Constructor Counter3 initializes the counter


* @param start - the starting value for the count */
public Counter3(int start)
{ count = start; }

/** increment updates the count and signals all Observers */


public void increment()
{ count = count + 1;
setChanged(); // marks that an event has occurred
notifyObservers(); } // signals all Observers that an event has occurred

/** countOf accesses count.


* @return the value of count */
public int countOf()
{ return count; }
}

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
}

/** update prints the newest value of the counter


* @param ob - the object that signalled the event (here, the counter)
* @param extra_arg - a parameter that we will not use */
public void update(Observable ob, Object extra_arg)
{ System.out.println("new count = " + count.countOf()); }
}

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

Figure 10.40: MVC-architecture with Observers and multiple views

Frame4a

Count3Button Counter3 Observer


Panel4a

Observable

10.12.1 Observers and the MVC-Architecture


A primary motivation for writing applications in Model-View-Controller style was to
decouple the application’s components so that they are easily modified and reused.
We have been most successful in this regard with the model components, which are
ignorant of the views and controllers that use them, and least successful with the
controllers, which know the identities of the model and all the views that depict the
model.
Applications with GUIs often have complex views that evolve while the application
executes, and it may be unrealistic to make a controller contact all the view objects
that must change after the controller sends a message to the model. In this situation,
it is better to make the controller ignorant of the views and make the views into
Observers of the model. For this reason, we learn how to employ Observers in MVC-
architectures.
Figure 19 showed a simple GUI that displayed two “views” of a counter—a numer-
ical view in a frame and a graphical view in a panel. The application that generated
the GUI appeared in Figure 20. It used a controller, class CountButton, that was
given the identity of both the counter and the frame. (See Figure 13.) When the user
pushed the CountButton, the button sent a message to the counter and a message to
the frame. The latter refreshed its numerical count and told the panel to repaint its
graphical presentation.
We can simplify the CountButton so that it knows nothing about view objects;
we also decouple the frame from the panel. We do these steps by registering the
frame and the panel as two Observers of the model. The MVC-architecture we design
appears in Figure 40. The Figure shows that the controller is decoupled from the
view; the views are notified indirectly to update themselves, through the Observer
interface. And, thanks to class Observable, the model remains decoupled from the
other components in the application.
Figure 41 presents the controller, Count3Button, and Figure 42 shows the view
classes, Frame4a and Drawing4a. Finally, Figure 43 gives the start-up class that creates
636

Figure 10.41: controller decoupled from views


import javax.swing.*;
import java.awt.event.*;
public class Count3Button extends JButton implements ActionListener
{ private Counter3 model; // see Figure 39

public Count3Button(String my label, Counter3 my model)


{ super(my label);
model = my model;
addActionListener(this);
}

public void actionPerformed(ActionEvent evt)


{ model.increment(); }
}

the application’s objects and registers them as the model’s Observers.


When an application with a GUI is said to have an “MVC-architecture,” it nor-
mally means that the components are connected as depicted in Figure 40—controllers
are decoupled from views, and views independently update themselves when contacted
by events generated by the model. Because of Java’s demand that a model extend
Observable, it might not be possible to use this pattern in all cases, however, so our
previous MVC-designs still have value.

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?

2. As in the previous example, convert the slide-puzzle application in Figures 24


and 25 so that its model has Observers.

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

Figure 10.42: view classes


import java.awt.*; import javax.swing.*; import java.util.*;
/** Frame4a is a frame with a label, button, and panel */
public class Frame4a extends JFrame implements Observer
{ private Counter3 count; // address of model object; see Figure 39
private JLabel lab = new JLabel("count = 0"); // label for the frame

/** 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);
}

/** update revises the label that displays the count */


public void update(Observable model, Object extra arg)
{ lab.setText("count = " + count.countOf()); }
}
638

Figure 10.42: view classes (concl.)


import java.awt.*; import javax.swing.*; import java.util.*;
/** Panel4a creates a panel that displays a small painting. */
public class Panel4a extends JPanel implements Observer
{ private Counter3 count;

public Panel4a(Counter3 model)


{ count = model;
setSize(200, 80);
}

/** update repaints the panel */


public void update(Observable model, Object extra arg)
{ repaint(); }

public void paint(Graphics g)


{ g.setColor(Color.white);
g.fillRect(0, 0, 150, 80);
g.setColor(Color.red);
for ( int i = 0; i != count.countOf(); i = i+1 )
{ g.fillOval(i * 25, 0, 20, 20); }
}
}

Figure 10.43: startup class for application


/** Example4a starts the application */
public class Example4a
{ public static void main(String[] args)
{ Counter3 model = new Counter3(0);
Panel4a panel = new Panel4a(model);
Frame4a frame = new Frame4a(model, panel);
model.addObserver(panel);
model.addObserver(frame);
}
}
10.13. SUMMARY 639

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: input for a program—can be a button push, typing of text, selecting a


menu item, etc. The graphical component—button, text field, or menu item—
that is used to cause the event is called the event source.

• event-driven programming: the style of programming one uses to receive and


react to events, typically using a collection of controller components, one that
does computation in reaction to each form of event.

• event handler (event listener): a controller component that receives and reacts
to an event; this is called handling the event.

• action event: an event caused by a button push or a menu selection. An action


event is handled by an event listener called an action listener.

• 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.

• container: a graphical component that can hold other components. A panel is


the usual form of container.

• window: a “top level” component that can be displayed.

• frame: a window with a title bar and menus. Meant to be

• dialog: a temporary window that can appear and disappear while an application
executes. There are three forms:

– message dialog: displays a warning or error message


– confirm dialog: displays a question that the user must answer by pressing
a button
– input dialog: displays a text field into which a user can type input text

• 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

• button: a component that can be pushed, triggering an action event

• list: a component that displays items that can be chosen (“selected”)

• menu: a component that, when selected, displays a sequence of menu items,


each of which can be selected, triggering an action event

• menu bar: a component that holds a collection of menus

• 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

• A programmer assembles a GUI as a composite of graphical components like


buttons, labels, and text fields. The components must be inserted into a frame’s
content pane, which must be told to use a particular layout for arranging the
components.

• 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.

• The graphical components in Java’s AWT/Swing framework have internal Model-


View-Controller (MVC) architectures, and a programmer often works with a a
graphical component by reading and updating its internal model. Indeed, for
components like text areas and lists, the component sometimes serves as the
10.14. PROGRAMMING PROJECTS 641

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.

New Classes for Later Use


Figure 5 lists the classes we use for constructing graphical user interfaces.

10.14 Programming Projects


1. Revise the bank-account manager application in Figure 9, Chapter 6, so that
its input- and output-view classes are combined into one pleasant-to-use GUI.

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.

4. Write an application that lets two humans play tic-tac-toe (noughts-and-crosses)


by clicking on the buttons of a 3-by-3 grid.

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| | | |
-----------

The computer responds with its own move:

| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | | | | | |
| | |X|O| | |
-----------

The human might respond by dropping a pebble on top of another:

| | | | | | |
| | | | | | |
| | | | | | |
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

others hold “berries.” (It is traditional that the Pac-Man be represented by a


smile-face that is ready to “eat,” e.g.,

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 Beyond the Basics


10.15.1 Applets
646

10.15.2 Tables and Spreadsheets

10.15.3 Handling Mouse Clicks and Drags

10.15.4 Threads of Execution

10.15.5 GUI Design and Use-Cases

10.15.6 Summary of Methods for Graphical Components

These optional sections described additional capabilities of the AWT/Swing frame-


work. The final section is a tabular summary of all the classes and methods encoun-
tered in the chapter.

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:

&lt;title>My Slide Puzzle&lt;/title>


&lt;body bgcolor=white>
Here is my slide puzzle as an applet:
&lt;p>
&lt;applet code = "PuzzleApplet.class" width=300 height=300>
Comments about the applet go here. This applet will be a simple
adaptation of class PuzzleFrame in Figure 24. &lt;/applet>
&lt;p>
&lt;/body>

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:

&lt;applet code = "PuzzleApplet.class" width=300 height=300>


&lt;param name="size" value="5">
Comments about the applet go here. This applet will be a simple
adaptation of class PuzzleFrame in Figure 24. &lt;/applet>

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();

The HTML parameter command creates a String-typed “actual parameter” that


is fetched by the getParameter method inside the applet’s init method. Multiple
parameters can be created this way, as long as each parameter is labelled uniquely in
the HTML file.
Finally, it is also possible to “detach” an applet from the web page that creates it
so that the applet’s GUI appears as a new application on the user’s display. Indeed,
this is also the easiest way to convert an application into an applet, because only the
header line of the main method changes; everything else remains the same:
• Replace the header line of the main method by public void init() in the fol-
lowing class:

import javax.swing.*;
public class ExampleApplet extends JApplet
{
public void init()
{ ... the body of the main method goes here ... }
}
648

Figure 10.44: slide puzzle applet


import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/** PuzzleApplet shows a slide puzzle, adapted from Figure 24 */
public class PuzzleApplet extends JApplet
{ private SlidePuzzleBoard board; // the board that is displayed
private int size; // the board’s size
private int button size = 60; // width/height of button
private PuzzleButton[][] button; // buttons for the positions on the board

/** init builds the interface */


public void init()
{ // these statements are moved here from the former main method:
size = 4; // a 4 x 4 slide puzzle
board = new SlidePuzzleBoard(size); // model
// the remainder of the constructor method stays the same:
button = new PuzzleButton[size][size];
Container cp = getContentPane();
...
// but remember to remove these statements:
// addWindowListener(new ExitController());
// setTitle("PuzzleFrame");
// setSize(size * button size + 10, size * button size + 20);
// setVisible(true);
}

/** update consults the model and redraws the puzzle grid. */
public void update()
{ ... } // as before; see Figure 24
}
10.15. BEYOND THE BASICS 649

• Within the HTML file, use this command:

&lt;applet code = "ExampleApplet.class" width=0 height=0>


This applet displays nothing within the Web page itself, but it generates
a view just like an ordinary application would do. &lt;/applet>

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);
}
}

If we start the PuzzleGeneratorApplet from this HTML file:


&lt;title>Slide Puzzle Generator&lt;/title>
&lt;body bgcolor=white>
Congratulations! You have just started the Slide Puzzle application!
&lt;applet code = "PuzzleGeneratorApplet.class" width=0 height=0>
&lt;/applet>
&lt;/body>

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.

10.15.2 Tables and Spreadsheets


The AWT/Swing framework contains a class JTable that makes relatively quick
work of creating and displaying tables and spreadsheets. Figure 45 displays a simple
GUI that contains a JTable component.
The view shows that a table is a grid, containing rows and columns. Each column
has its own label. Indeed, the table’s view lets a user “stretch” a column’s width by
moving the mouse to the right edge of the column’s label and dragging the label’s
right edge to the desired width. This trick is helpful for reading extra long strings
that might be displayed in the column’s cells.
The table’s rows are numbered 0, 1, 2, etc.; the labels above the columns are not
included in the row numbering. For example, in Figure 45 the string, George Wallace,
appears in the cell at Row 2, Column 0; the label, Illinois, appears above Column
1.
650

Figure 10.45: a simple JTable

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

Figure 10.46: interface TableModel


public interface TableModel
{ /** getColumnCount returns the number of columns in the table */
public int getColumnCount();

/** getColumnName returns the label for Column j of the table */


public String getColumnName(int j);

/** getRowCount returns the number of rows in the table */


public int getRowCount() ;

/** getValueAt returns an object that contains the value at Row i,


Column j, within the table */
public Object getValueAt(int i, int j);

/** setValueAt updates Row i, Column j, of the table to have value v */


public void setValueAt(v, i, j);

/** 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);

/** addTableModelListener adds a listener to the table */


public void addTableModelListener(TableModelListener l) ;

/** removeTableModelListener removes a listener from the table */


public void removeTableModelListener(TableModelListener l)

/** getColumnClass returns the data type of the objects held in Column j */
public Class getColumnClass(int j);
652

Figure 10.47: model for vote table


import javax.swing.table.*;
public class VoteModel extends AbstractTableModel
{ // The table will be displayed with the region names across the top.
// Candidate i’s name appears at table cell Row i, Column 0;
// Candidate i’s votes from Region j appear at table cell Row i, Column j+1
private String[] region; // regions’ names
private String[] candidate; // candidates’ names
private int[][] votes; // the votes for candidate i, region j,

public VoteModel(String[] person, String[] district)


{ super();
candidate = person;
region = district;
votes = new int[candidate.length][region.length];
}

public int getRowCount()


{ return candidate.length; }

public int getColumnCount()


{ return region.length + 1; }

public Object getValueAt(int table row, int table column)


{ Object result = null;
if ( table column == 0 ) // is it a the candidate’s name?
{ result = candidate[table row]; }
else { result = new Integer(votes[table row][table column-1]); }
return result;
}

public String getColumnName(int column)


{ String answer = "";
if ( column == 0 )
{ answer = "Candidate"; }
else { answer = region[column-1]; }
return answer;
}

/** getVoteCount returns the votes for person in district */


public int getVoteCount(int person, int district)
{ return votes[person][district]; }
...
10.15. BEYOND THE BASICS 653

Figure 10.47: model for vote table (concl.)


/** changeVoteCount updates person’s votes in district to new count
* and signals the table’s listeners that the table has changed */
public void changeVoteCount(int person, int district, int new count)
{ votes[person][district] = new count;
fireTableDataChanged();
}
}

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.

Updating the Table


The application constructed in Figure 48 is useless, because there is no means of
casting votes and updating the table model. So, we improve the application so that
a user can vote from a region for a candidate, and we improve the table model so
that it remembers both the votes cast as well as candidates’ and regions’ totals. The
revised table model will be displayed as seen in Figure 49, and a secondary frame will
appear in which a user can select one region and one candidate and cast one vote.
The Figure shows that six votes have already been cast in the election, and the
seventh vote is ready to be entered.
The hardest work in this application is extending VoteModel in Figure 47 so that
its getValueAt method can correctly calculate the total of a row or the total of a
column when asked. Figure 50 shows the result, called class VoteModelWithTotals.
The getValueAt must handle requests for the values in the extra row and the
extra column; all other requests it sends to the getValueAt method in its superclass.
Notice that getColumnName is extended in a simple way to return the label for the
extra column.
Figure 51 shows the view class for casting votes, and Figure 52 includes the con-
troller and start-up class for the application.

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

Figure 10.48: view for displaying the vote-count table


import java.awt.*;
import javax.swing.*;
/** VoteFrame displays a table of votes */
public class VoteFrame extends JFrame
{
/** VoteFrame constructs the view
* @param model - a table model */
public VoteFrame(VoteModel model)
{ JTable vote table = new JTable(model); // embed the table model in a JTable
JScrollPane pane = new JScrollPane(vote table);
Container cp = getContentPane();
cp.setLayout(new BorderLayout());
cp.add(pane, BorderLayout.CENTER);
setSize(500, 120);
setTitle("Presidential Vote Table");
setVisible(true);
}
}

/** Vote1 starts the application */


public class Vote1
{ public static void main(String[] args)
{ String[] candidates = {"Hubert Humphrey", "Richard Nixon",
"George Wallace"};
String[] states = {"Illinois", "Idaho", "Iowa", "Ohio"};
VoteModel model = new VoteModel(candidates, states);
VoteFrame view = new VoteFrame(model);
}
}
10.15. BEYOND THE BASICS 655

Figure 10.49: vote table with totals and voting frame

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.

10.15.3 Handling Mouse Clicks and Drags


AWT/Swing provides a basic collection of mouse events that can be handled:

• When a mouse is moved into a component (e.g., a frame or a panel, or a text


656

Figure 10.50: table model that computes vote totals


/** VoteModelWithTotals is a table model that computes votes and totals */
public class VoteModelWithTotals extends VoteModel
{ // The expanded model has one extra column (the total votes for each
// candidate) and one extra row (the total votes cast in each region)
private int number of candidates;
private int number of regions;

public VoteModelWithTotals(String[] candidate, String[] region)


{ super(candidate, region);
number of candidates = candidate.length;
number of regions = region.length;
}

public int getRowCount()


{ return super.getRowCount() + 1; }

public int getColumnCount()


{ return super.getColumnCount() + 1; }

public Object getValueAt(int table row, int table column)


{ Object result = null;
if ( table column == (number of regions + 1) ) // a candidate’s vote total?
{ if ( table row < number of candidates )
{ result = new Integer(computeTotalForPerson(table row)); }
else { result = ""; }
}
else if ( table row == number of candidates ) // total votes in a region?
{ if ( table column > 0 )
{ result = new Integer(computeTotalForRegion(table column-1)); }
else { result = "Votes cast in state:"; }
}
else { result = super.getValueAt(table row, table column); }
return result;
}

public String getColumnName(int column)


{ String answer = "";
if ( column == (number of regions + 1) ) // is it the last column?
{ answer = "Total"; }
else { answer = super.getColumnName(column); }
return answer;
}
...
10.15. BEYOND THE BASICS 657

Figure 10.50: table model that computes vote totals (concl.)


/** computeTotalForPerson totals all votes for candidate who */
private int computeTotalForPerson(int who)
{ int total = 0;
for ( int j = 0; j != number of regions; j = j+1 )
{ total = total + super.getVoteCount(who, j); }
return total;
}

/** computeTotalForRegion totals all votes cast in region where */


private int computeTotalForRegion(int where)
{ int total = 0;
for ( int i = 0; i != number of candidates; i = i+1 )
{ total = total + super.getVoteCount(i, where); }
return total;
}
}

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.

Figure 55 lists interface MouseListener, which a class must implement to handle


the mouse events listed above. The methods of a mouse listener react to mouse events.
Table 56 lists some useful methods for extracting information from a mouse event.
A class that implements MouseListener might not wish to handle all forms of
mouse events. There is a default listener, called MouseAdapter, which implements
MouseListener with “do nothing” event handlers for all mouse events. The simplest
way to build a mouse listener with limited abilities is to extend MouseAdapter.
Here is a simple example: Each time the mouse is clicked over a frame, the frame’s
mouse listener prints the position where the mouse was clicked. The class that handles
the mouse clicks can be written this simply:
import java.awt.event.*;
import javax.swing.*;
public class TestListener extends MouseAdapter
{ public TestListener(JFrame f)
658

Figure 10.51: view for vote casting


import java.awt.*; import java.awt.event.*; import javax.swing.*;
/** CastVoteFrame displays a frame that lets a user cast a vote */
public class CastVoteFrame extends JFrame
{ JList state list; // the region names from which a user selects
JList candidate list; // the candidates from which a user selects

/** CastVoteFrame constructs the view


* @param states - the names of the regions a voter can choose
* @param candidates - the names of the candidates a voter can choose
* @param b - the button the user pushes to cast a vote */
public CastVoteFrame(String[] states, String[] candidates, VoteButton b)
{ Container cp = getContentPane();
cp.setLayout(new BorderLayout());
JPanel p1 = new JPanel(new GridLayout(1,2));
state list = new JList(states);
p1.add(state list);
candidate list = new JList(candidates);
p1.add(candidate list);
cp.add(p1, BorderLayout.CENTER);
JPanel p2 = new JPanel(new FlowLayout());
p2.add(b);
b.setViewTo(this);
cp.add(p2, BorderLayout.SOUTH);
pack();
setTitle("Cast a vote");
setVisible(true);
}

/** 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

Figure 10.52: controller and start-up class for voting application


import java.awt.*; import java.awt.event.*; import javax.swing.*;
/** VoteButton implements a button that computes.... */
public class VoteButton extends JButton implements ActionListener
{ private VoteModel model; // the table model that is updated
private CastVoteFrame frame; // the frame from which the vote is extracted

public VoteButton(String label, VoteModel m)


{ super(label);
model = m;
addActionListener(this);
}

public void setViewTo(CastVoteFrame f)


{ frame = f; }

public void actionPerformed(ActionEvent evt)


{ int[] vote = frame.getInputs();
if ( vote[0] != -1 && vote[1] != -1 )
{ int count = model.getVoteCount(vote[0], vote[1]);
model.changeVoteCount(vote[0], vote[1], count + 1);
}
else { JOptionPane.showMessageDialog(frame,
"State and Candidate not properly selected---vote ignored");
}
}
}

public class Vote2


{ public static void main(String[] args)
{ String[] candidates = {"Hubert Humphrey", "Richard Nixon",
"George Wallace"};
String[] states = {"Illinois", "Idaho", "Iowa", "Ohio"};

VoteModelWithTotals model = new VoteModelWithTotals(candidates, states);


VoteFrame view = new VoteFrame(model);
VoteButton controller = new VoteButton("Vote", model);
CastVoteFrame frame = new CastVoteFrame(states, candidates, controller);
}
}
660

Figure 10.53: a voting spreadsheet

{ super();
f.addMouseListener(this); // registers this object as a listener of f
}

public void mouseClicked(MouseEvent e)


{ System.out.println("Mouse clicked at " + e.getX() + ", " + e.getY()); }
}

For a frame, v, we state

new TestListener(v)

and this connects the listener to v.


Mouse listeners work well at helping users draw on panels and frames. For exam-
ple, we might write an application that translates mouse clicks into tiny red and blue
boxes and converts mouse drags into yellow ovals:
10.15. BEYOND THE BASICS 661

Figure 10.54: table model spreadsheet


public class VoteSpreadsheet extends VoteModelWithTotals
{ public VoteSpreadsheet(String[] candidate, String[] region)
{ super(candidate, region); }

/** 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
}
}

/** Vote3 starts the spreadsheet application. */


public class Vote3
{ public static void main(String[] args)
{ String[] candidates = {"Hubert Humphrey", "Richard Nixon",
"George Wallace"};
String[] states = {"Illinois", "Idaho", "Iowa", "Ohio"};
VoteSpreadsheet model = new VoteSpreadsheet(candidates, states);
VoteFrame view = new VoteFrame(model);
}
}
662

Figure 10.55: interface for mouse listeners


public interface MouseListener
{ /** mouseClicked handles one or more clicks of a stationary mouse */
public void mouseClicked(MouseEvent e)

/** 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 10.56: methods of a mouse event

class MouseEvent an event caused by moving a mouse or pressing its


button
Methods
getClickCount(): int Return the number of clicks the user made with the
mouse.
getX(): int Return the x-position where the mouse event oc-
curred.
getY(): int Return the y-position where the mouse event oc-
curred.
10.15. BEYOND THE BASICS 663

Figure 10.57: view for drawing shapes on a frame


import java.awt.*; import javax.swing.*;
/** MouseView lets a user draw yellow ovals and tiny colored boxes */
public class MouseView extends JPanel
{ private int width; // the panel’s width
private int depth; // the panel’s depth

/** MouseView builds the panel with width w and depth d */


public MouseView(int w, int d)
{ width = w; depth = d;
JFrame my frame = new JFrame();
my frame.getContentPane().add(this);
my frame.setSize(width, depth);
my frame.setTitle("Mouse tester");
my frame.setVisible(true);
}

public void paintComponent(Graphics g)


{ g.setColor(Color.white);
g.fillRect(0, 0, width, depth);
}

/** clear erases the frame */


public void clear()
{ repaint(); }

/** paintBox paints a small box of color c at position x pos, y pos */


public void paintBox(int x pos, int y pos, Color c)
{ Graphics g = getGraphics();
g.setColor(c);
g.fillRect(x pos, y pos, 10, 10);
}

/** paintOval paints an oval at x pos, y pos of size width by depth */


public void paintOval(int x pos, int y pos, int width, int depth)
{ Graphics g = getGraphics();
g.setColor(Color.yellow);
g.fillOval(x pos, y pos, width, depth);
g.setColor(Color.black);
g.drawOval(x pos, y pos, width, depth);
}
}
664

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.

10.15.4 Threads of Execution


How can one application run two animations simultaneously? The throbbing-ball
animation in Figure 23 showed that one animation can operate, provided that the
controller whose loop runs the animation executes last. (See the last statement of
main in class StartThrob, Figure 25.) But if there are two animations to control,
both controllers cannot be “last.”
The underlying problem is a Java application has one sequence, or “thread,” of
instructions to execute. If the thread of instructions leads to a nonterminating loop,
the instructions that follow the loop will never be performed. Therefore, to operate
two nonterminating loops, two threads of execution are necessary.
The Java language makes it possible to give an object its own thread of execution,
so that the object executes as if it were a separate application. More precisely, the
object must have a method named run, and when the object’s own thread of execution
is created, the object’s run method executes.
We can use multiple threads with the throbbing-ball animation so that two balls
throb simultaneously. The model class, ThrobbingBall, and its view, ThrobPanel
(Figure 24), are left intact. The first key modification is adding the phrase, implements
Runnable to the header line of class ThrobController in Figure 25:
10.15. BEYOND THE BASICS 665

Figure 10.58: controller for mouse clicks and drags


import java.awt.*;
import java.awt.event.*;
/** MouseController controls drawing based on mouse clicks */
public class MouseController extends MouseAdapter
{ private MouseView view; // the view object where shapes are drawn

public MouseController(MouseView f)
{ view = f;
view.addMouseListener(this);
}

/** mouseClicked handles a single click by drawing a small box;


* it handles a multiple click by clearing the view
* @param e - the event that remembers the position and number of clicks */
public void mouseClicked(MouseEvent e)
{ if ( e.getClickCount() > 1 ) // a ‘‘double click’’?
{ view.clear(); } // then, clear the view
else { int x = e.getX(); // else, draw a box
int y = e.getY();
Color c = Color.red;
if ( (x + y)% 2 == 0 ) // for fun, use blue on even pixel values
{ c = Color.blue; }
view.paintBox(x, y, c);
}
}

private int start x; // remembers the position where the user


private int start y; // started to drag the mouse

/** mousePressed remembers the start position of a mouse drag


* @param e - the event that remembers the position */
public void mousePressed(MouseEvent e)
{ start x = e.getX();
start y = e.getY();
}
...
666

Figure 10.58: controller for mouse clicks and drags (concl.)


/** mouseReleased draws an oval based on the end position of the mouse drag
* @param e - the event that remembers the end position */
public void mouseReleased(MouseEvent e)
{ int new x = Math.min(start x, e.getX()); // compute upper left corner
int new y = Math.min(start y, e.getY());
int width = Math.abs(start x - e.getX()); // compute absolute size
int depth = Math.abs(start y - e.getY());
view.paintOval(new x, new y, width, depth);
}

/** test the controller with a view: */


public static void main(String[] args)
{ new MouseController(new MouseView(300, 200)); }
}

public class ThrobController implements Runnable


{ ...
public void run()
{ ... }
...
}

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

/** StartThrobs builds two animation panels and displays them. */


public class StartThrobs
{ public static void main(String[] a)
{ int panel_size = 180;
ThrobbingBall b1 = new ThrobbingBall();
ThrobPanel w1 = new ThrobPanel(panel_size, b1);
ThrobbingBall b2 = new ThrobbingBall();
ThrobPanel w2 = new ThrobPanel(panel_size, b2);
new Throb2Frame(200, w1, w2);
new Thread(new ThrobController(w1, b1, 200)).start();
new Thread(new ThrobController(w2, b2, 300)).start();
System.out.println("Animations have started!");
}
}

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

public Tickets(int initial_value)


{ how_many_left = initial_value; }

public boolean ticketsAvailable()


{ return how_many_left > 0; }

public int sellTicket()


{ int ticket_number = how_many_left;
how_many_left = how_many_left - 1;
return ticket_number;
}
}
And here is a sales agent:
public class SalesAgent implements Runnable
668

{ private int id; // the sales agent’s ‘‘name’’


private Tickets ticket_source; // the object that has the tickets
private int time; // the time the agent pauses between sales

public SalesAgent(int i, Tickets t, int speed)


{ id = i;
ticket_source = t;
time = speed;
}

public void run()


{ int i;
while ( ticket_source.ticketsAvailable() )
{ i = ticket_source.sellTicket();
System.out.println("Agent " + id + " sells ticket " + i);
delay(); // pause so that other threads might resume
}
System.out.println("Agent" + id + " is finished");
}

private void delay()


{ try{ Thread.sleep(time); }
catch (InterruptedException e) {}
}
}

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.

10.15.5 GUI Design and Use-Cases


Most of the previous discussion on program design has focussed on model construc-
tion. With the inclusion of GUIs, the design of the view becomes equally important
to a program’s success. We begin by repeating the guidelines for GUI design given
at the beginning of this Chapter:

• Organize sequences of events into natural units for processing.

• 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.

1. The user submits 10.50 to the GUI.

2. The user indicates the amount is a deposit.

3. The user signals that the transaction should be made.

4. Computation occurs, and an acknowledgement, along the with account’s new


balance, appears on the GUI.

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:

1. The user submits 10000.00 into the GUI.

2. The user indicates the amount is a withdrawal.

3. The user signals that the transaction should be made.

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:

1. Without submitting a numerical amount, the user signals that a withdrawal


should be done.

2. The application reports an error.

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

To develop a representative set of use-cases, a designer must discuss the application


with its users, study the problem area and its past solutions, and conceive “thought
experiments” about what might happen when the application is used.

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.

10.15.6 Summary of Methods for Graphical Components

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 Container extends a component that can hold other components


Component
Methods
setLayout(LayoutManager Tells the container to use layout to arrange the
layout) components that are inserted into it.
add(Component ob) Inserts ob inside the container at the end of its
layout.
add(Component ob, Object Inserts ob inside the container, using the
restriction) restriction to determine ob’s position in the
layout. (See class BorderLayout for examples
of restrictions.)

class Window extends a top-level component that can be displayed by


Container itself
Methods
pack() Sets the size of the window just large enough
that it displays all the components inserted into
it. (Note: Empty panels are not always dis-
played at full size; use setSize in this case.)
dispose() Removes the window from the display and
makes it impossible to display it again.
Registers listener as a listener (event handler)
addWindowListener(WindowListener
listener) for window events generated by this object.

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.

class JApplet extends a top-level component that can be displayed in


Container a Web page
Methods
init() Initializes the applet immediately after the ap-
plet is constructed.
getParameter(String s): Fetches from the applet command that started
String the applet the actual parameter labelled by s
getContentPane(): Container Returns the applet’s content pane, into which
components can be inserted.
setJMenuBar(JMenuBar mbar) Attaches menu bar mbar to the top of the applet.
674

abstract class JComponent the basic graphical object in the Swing frame-
extends Container work

abstract class Abstract a component that can be “pushed” or “set” or


Button extends JComponent “selected” by a user
Methods
Attaches listener to this button as its listener
addActionListener(ActionListener
listener) (event handler) for action events.
setEnabled(boolean b) Sets whether or not the button generates an
action event when it is pushed.
isEnabled(): boolean Returns whether or not the button generates an
action event when it is pushed.

getText(): String Returns the text that appears on the button’s


face in the view.
setText(String s) Sets the text that appears on the button’s face
to s.
doClick() Generates an action event, just like when the
user pushes the button.
isSelected(): boolean Returns whether or not the button is “selected,”
that is, pushed inwards.
setSelected(boolean b) Sets whether or not the button is “selected,”
that is, pushed inwards.

class JButton extends a button


AbstractButton
Constructor
JButton(String s) Constructs a button whose face displays s.

class JMenuItem extends a menu item


AbstractButton
Constructor
JMenuItem(String s) Constructs a menu item whose face displays s.

class JMenu extends a “container” for menu items


JMenuItem
Constructor
JMenu(String s) Constructs a menu whose face displays s.
Methods
add(Component c) Adds component c (normally, a menu or menu
item) to the end of this menu.
addSeparator() Adds a separator bar to the end of this menu.
10.15. BEYOND THE BASICS 675

class JLabel extends a component that displays a string


JComponent
Constructor
JLabel(String s) Constructs a label that displays s.
Methods
getText():String Returns the text displayed by the label.
setText(String s) Sets the label to display s.

class JList extends a sequence of items that can be selected by the


JComponent user
Constructor
JList(Object[] items) Constructs a list whose internal model is the
array, items. The elements of items must be
objects that possess a method, toString():
String. (Note: String objects have this
method by default.) The items are indexed 0,
1, 2, etc.
Methods
setSelectionMode(int mode) Sets whether at most one
(ListSelectionModel.SINGLE SELECTION)
or more than one
(ListSelectionModel.MULTIPLE INTERVAL SELECTION)
item can be selected from the list by the user.
getSelectedIndex(): int Returns the index number of the item currently
selected. (If no item is selected, -1 is returned.
If multiple items are selected, the lowest num-
bered index is returned.)
getSelectedIndices(): int[] Returns an array whose elements are the index
numbers of all items currently selected.
setSelectedIndex(int i) Makes item i of the list appear selected in the
list’s view.
setSelectedIndices(int[] r) Makes all the items in array r appear selected
in the list’s view.
clearSelection() Unselects all selected items.
Registers listener as a listener (event handler)
addListSelectionListener(ListSelectionListener
listener) for list selection events within this object.
676

class JMenuBar extends a component to which menus are attached


JComponent
Constructor
JMenuBar() Constructs a menu bar.
Methods
add(JMenu m) Attaches menu m to the menu bar.

class JOptionPane extends displays dialogs


JComponent
Methods
showMessageDialog(Component Displays a message dialog (that is, a dialog that
owner, Object message) displays message, normally a string, and an OK
button) that suspends the execution of owner
until the user dismisses the dialog.
showConfirmDialog(Component Displays a confirm dialog (that is, a dialog
owner, Object message): int that displays message, normally a string,
and three buttons, Yes, No, and Cancel).
The dialog suspends the execution of owner
until the user dismisses it. When dis-
missed, the dialog returns one of these four
integer results: JOptionPane.YES OPTION,
JOptionPane.NO OPTION,
JOptionPane.CANCEL OPTION, or
JOptionPane.CLOSED OPTION. (The last oc-
curs when the user pushes the “X”-button to
terminate the dialog.)
showInputDialog(Component Displays an input dialog (that is, a dialog that
owner, Object message): displays message, a text field, and two buttons,
String OK and Cancel). The dialog suspends the execu-
tion of owner until the user dismisses it. When
dismissed by a push of Ok, the dialog returns the
contents of the text field. Otherwise, the dialog
returns null.

class JPanel extends the simplest possible JComponent


JComponent
Constructors
JPanel() Constructs a panel.
JPanel(LayoutManager layout) Constructs a panel that uses layout to arrange
its components.
10.15. BEYOND THE BASICS 677

class JScrollPane extends a scroll bar that attaches to a component


JComponent
Constructor
JScrollPane(Component c) Wraps a scroll bar around c.

abstract class a component that holds user-editable text


JTextComponent extends
JComponent
Methods
getText(): String Returns, as one string, the entire contents of
the text component
setText(String s) Resets the contents of the text component to s.
getCaretPosition(): int Returns the position of the insertion caret
within the text component.
setCaretPosition(int i) Repositions the insertion caret to position i, a
nonnegative integer.
moveCaretPosition(int i) “Drags” the caret from its exiting position to
position i, in the process selecting all the text
that falls between the old and new positions.
getSelectedText(): String Returns the text that is selected within the text
component.
getSelectionStart(): int Returns the position where the selected text be-
gins.
getSelectionEnd(): int Returns the position where the selected text
ends.
replaceSelection(String s) Replaces the currently selected text by s.
cut() Removes the selected text from the text com-
ponent and copies it into the component’s clip-
board.
copy() Copies the selected text into the component’s
clipboard.
paste() Copies the text in the component’s clipboard
into the text component at the position marked
by the insertion caret.
isEditable(): boolean Returns whether or not the user is allowed to
alter the contents of the text component.
setEditable(boolean b) States whether or not the user is allowed to alter
the contents of the text component.
678

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 JTextArea extends a text component that holds multiple lines of


JTextComponent text
Constructor
JTextArea(String s, int Constructs a text area of size rows by columns
rows, int columns) whose initial value is s.
Methods
setLineWrap(boolean b) State whether text lines that are longer than
the text area’s width should be “wrapped” to
appear on the next line.
insert(String s, int i) Insert s at position i within the text area.
replaceRange(String s, int Remove the text starting at position start and
start, int end) ending at position end - 1 and replace it by s.

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

class WindowEvent an event generated by the user opening, closing,


or adjusting the size of a window
Method
getWindow(): Window Return the address of the window where this
event occurred.

class ActionEvent an event caused by the user clicking a button


or selecting a menu item
Methods
getSource(): Object Return the address of this event’s source.
getActionCommand(): String Return the text that appears on the face of the
event source object.

class ListSelectionEvent an event caused by the user selecting a list item


Methods
getFirstIndex(): int Return the index of the first of the items whose
selection generated this event.
getLastIndex(): int Return the index of the last of the items whose
selection generated this event.
Chapter 11

Text and File Processing


11.1 Strings are Immutable Objects
11.1.1 String Tokenizers
11.2 Sequential Files
11.2.1 Output to Sequential Files
11.2.2 Input from Sequential Files
11.3 Sequential Input from the Command Window
11.4 Case Study: Payroll Processing
11.5 Exceptions and Exception Handlers
11.5.1 Restarting a Method with an Exception Handler
11.5.2 Interactive Input with Exception Handlers
11.6 Exceptions Are Objects
11.6.1 Programmer-Generated Exceptions
11.7 Summary
11.8 Programming Projects
11.9 Beyond the Basics
The applications presented in previous chapters accepted textual input by means of
the user typing it into a window, one data item per line. Output was displayed in a
similar way. Window-based input and output are inadequate for programs that must
create and maintain large amounts of textual data, where multiple data items reside
in each textual line and multiple lines of text reside in disk files. In this chapter, we
study the following techniques:
• how to decompose a line of text that contains multiple data items into its con-
stituent parts, called tokens, by means of string tokenizers;
• how to read and write lines of text to and from sequential disk files;
• how to adapt the same techniques to read lines of text from the command win-
dow;
• how to use exception handlers to deal with errors in file processing.
Because we work with files of text strings, we first examine how strings are represented
in computer storage.
11.1. STRINGS ARE IMMUTABLE OBJECTS 673

11.1 Strings are Immutable Objects


Since Chapter 2, we have worked with strings, which are represented in Java as
sequences of characters enclosed by double quotes, e.g., "abcd". Special characters,
like backspaces, tabs, and newlines, can be encoded within a string with these codings:
• \b (backspace)
• \t (tab)
• \n (newline)
• \r (return)
• \" (doublequote)
• \’ (single quote)
• \\ (backslash)
For example, System.out.println("a\tb\nc\"d") would print as
a b
c"d
In previous chapters, we have systematically ignored the fact that string are ob-
jects in Java; there was no need to worry about this point. For the curious, we now
reveal that a string is in fact saved in computer storage as an array of characters.
The initialization,
String s = "abcd";
creates this object in storage:
a1 : String
String s == a1
’a’ ’b’ ’c’ ’d’

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

char[] r = new char[2];


r[0] = ’a’;
r[1] = ’b’;
String u = new String(r);
String v = "ab";

The above statements create the string, "ab", and assign its address to t:

char[ ] r == a2 String u == a3 String v == a4

a2 : char[2] a3 : String a4 : String

’a’ ’b’ ’a’ ’b’ ’a’ ’b’

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.

Unlike an array of characters, you cannot assign to a character (element) of a string.


And when we write, say, String u = s + "e", this creates a new string object; it does
not alter the string denoted by s:

String s == a1 String u == a6

a1 : String a5 : String a6 : String

’a’ ’b’ ’c’ ’d’ ’e’ ’a’ ’b’ ’c’ ’d’ ’e’

At this point, if we assigned,


s = u;

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:

1. public boolean stringEquals(String s, String t) returns true is strings s


and t hold exactly the same sequences of characters and returns false otherwise.

2. public String extractSubstring(String s, int start, int end) returns the


string within s that begins at index start and ends at index end - 1. If such
a substring does not exist, because either of start or end has an invalid value,
then null is returned.

3. public String trimBlanks(String s) returns a string that looks like s but all
leading blanks and all trailing blanks are removed.

11.1.1 String Tokenizers


Our applications that read input have been designed to read one word—one token—
per input line. But it is more convenient for a programmer to type multiple tokens on
the same line. For example, a person would prefer to type her first, middle, and last
names on one line rather than on three. And a user of the change-making program in
Figure 2, Chapter 4, would prefer to type $13.46 as the program’s input rather than
13 on one line and 46 on another.
Of course, we can write an auxiliary method that decomposes a string into its
tokens, but this task arises often, so the Java designers wrote a helper class, class
StringTokenizer (found in the package, java.util), that contains methods for de-
composing strings into tokens.
Perhaps we have a string, s, that contains several tokens that are separated by spe-
cial characters, called delimiters. For example, a string, Lucy MacGillicutty Ricardo,
contains the tokens Lucy, MacGillicutty, and Ricardo, separated by blank spaces; the
blank serves as the delimiter. We construct a string tokenizer object that extracts
the tokens as follows:
676

String s = "Lucy MacGillicutty Ricardo";


StringTokenizer t = new StringTokenizer(s, " ");

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"

(Note: strings will no longer be presented text == "Lucy MacGillicutty Ricardo"


as arrays; we understand this implicitly)
" "
delimiters ==
StringTokenizer t == a1

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"

text == " MacGillicutty Ricardo"


StringTokenizer t == a1
" "
delimiters ==
String first name == "Lucy"

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

Figure 11.1: string tokenizer methods

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

int i = new Integer( y.nextToken() ).intValue()


+ new Integer( y.nextToken() ).intValue();
System.out.println(s + i);
(b) String x = "12!3,4";
StringTokenizer y = new StringTokenizer(x, ",");
String s = y.nextToken();
int i = new Integer( y.nextToken() ).intValue();
StringTokenizer t = new StringTokenizer(s,"!");
String a = t.nextToken();
int j = new Integer( t.nextToken() ).intValue();
System.out.println(i+j);

2. Revise Figure 3, Chapter 3, so that the MakeChange3 application reads its input
as follows,

DialogReader reader = new DialogReader();


reader.readString("Type a dollars, cents amount for change: $");

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.

11.2 Sequential Files


Computer programs can read input from and write output to disk files. Simply stated,
a file is a sequence of symbols stored on a diskette or on a computer’s internal disk.
There are two formats of files:

• a character file, which is a sequence of keyboard symbols, saved in the file as a


sequence of bytes;

• a binary file, which is a sequence of ones-and-zeros.

A file is indeed one long sequence; for example, these three text lines,

Hello to you!
How are you?
49

would be stored as a character file as this long sequence:

Hello to you!\nHow are you?\n 49\n


11.2. SEQUENTIAL FILES 679

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.,

Hello to you!\r\nHow are you?\r\n 49\r\n

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

• output: the file is written sequentially and cannot be read.


It is possible for a program to open a sequential file, read from it, close it, and then
reopen it and write to it. If one desires a file that one can open and both read and
write at will, then one must use a random-access file.
From this point onwards, we use the term sequential file to mean a sequential file
of (codings of) characters. We will not deal with binary files in this text.

11.2.1 Output to Sequential Files


The System.out object used methods named print and println to write data to the
console. We do something similar with sequential files.
The package, java.io, contains classes that have methods for file input and output.
To write to a sequential character file, we construct an object that connects to the
file and deposits text into it; say that the file that will hold the output is named
test.txt:

1. First, we construct the object,

Filewriter w = new FileWriter("test.txt");

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:

PrintWriter outfile = new PrintWriter(w);

The PrintWriter object accepts print(E) and println(E) messages, where it


translates the argument, E, into a sequence of characters and sends the charac-
ters, one by one, to the FileWriter object, w. For example,

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 11.2: sequential file output


import java.io.*;
/** Output1 writes three lines of characters to file test.txt */
public class Output1
{ public static void main(String[] args) throws IOException
{ PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));
outfile.println("Hello to you!");
outfile.print("How are");
outfile.println(" you?");
outfile.println(47+2);
outfile.close();
}
}

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 former contents of test.txt, if any, were destroyed.


The program begins with import java.io.*, which is the package where the new
classes live, and main’s header line states throws IOException, which the Java com-
piler requires whenever file input/output appears. The phrase is a warning that that
output transmission via might lead to an error (exception), e.g., the disk drive fails
in the middle of output transmission. IOExceptions can be processed with exception
682

handlers, which we first studied in Chapter 4. We employ exception handlers later in


the chapter.
It is perfectly acceptable to use multiple output files within the same program,
and it is done in the expected way:
PrintWriter file1 = new PrintWriter(new FileWriter("test.txt"));
PrintWriter file2 = new PrintWriter(new FileWriter("AnotherFile.out"));
...
file1.println("abc");
file2.print("c");
file2.println();
file1.close();
...

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.

11.2.2 Input from Sequential Files


For sequential file input, we must again construct an input-view object that connects
to the sequential file that holds the input text. This is again constructed in two
stages:
1. First, we construct an object that holds the file’s physical address and reads
individual characters from the file:

FileReader r = new FileReader("test.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.)

2. Next, we compose the FileReader object with a BufferedReader object, that


has a method that reads one full text line:
11.2. SEQUENTIAL FILES 683

Figure 11.3: program that copies a file


import java.io.*;
/** CopyFile copies the contents of an input file, f,
* whose name is supplied by the user, into an output file, f.out */
public class CopyFile
{ public static void main(String[] args) throws IOException
{ DialogReader reader = new DialogReader(); // see Fig. 10, Ch. 5
String f = reader.readString("Input filename, please: ");
// construct the view object that reads from the input file:
BufferedReader infile = new BufferedReader(new FileReader(f));
// construct the view object that writes to the output file:
PrintWriter outfile = new PrintWriter(new FileWriter(f + ".out"));
while ( infile.ready() ) // are there more lines to read in infile?
{ String s = infile.readLine();
outfile.println(s);
}
infile.close();
outfile.close();
}
}

BufferedReader infile = new BufferedReader(r);

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.

As an example, we write an application that reads the contents of a sequential


file whose name is supplied by the user at the keyboard and copies the file, f, line by
line, into another file, f.out. Figure 3 shows the application, class CopyFile.
The statement, infile = new BufferedReader(new FileReader(f)), creates a Buffered
Reader object that remembers the address of the filename, f, supplied by the user. In
a similar fashion, outfile = new PrintWriter(new FileWriter(f + ".out")) creates
an object that knows the address of the output file.
The while-loop within main uses a new method, infile.ready(), to determine if
there are lines of text waiting to be read from infile. If so, then another line is
read by s = infile.readLine() and is written by outfile.println(s). (Note: if the
readLine method attempts to read a string from a file that is exhausted, it returns
null as its result.)
Errors can arise when we use disk files. For example, say that we tell the program
to read a nonexistent file, fred. The program stops at the assignment to infile (Line
10) and announces:
684

java.io.FileNotFoundException: fred (No such file or directory)


...
at java.io.FileReader.<init>(...)
at CopyFile.main(CopyFile.java:10)

The error, java.io.FileNotFoundException, is an example of an IOException—the


program has “thrown” an exception, as was warned in the header line of main. Such
exceptions can be handled by exception handlers, as we see in the examples in the
next section.

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.)

11.3 Sequential Input from the Command Win-


dow
Since the beginning of this text, we used System.out.println(E) to display messages,
E, within the command window. It is also possible to read input text that the user
types into the command window—we use a preexisting object, System.in, which
connects to the command window, along with the techniques seen in the previous
section.
Here is an example: Recall that the change-making application, MakeChange,
from Figure 3, Chapter 3, calculates the change one extracts from dollars-and-cents
amounts. We can revise this program so the user starts it first and decides next what
the dollars and cents inputs might be. When the revised application is started, this
11.3. SEQUENTIAL INPUT FROM THE COMMAND WINDOW 685

request appears in the command window:

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:

In Java, System.in is the object that is connected to the computer’s keyboard


for interactive input. Unfortunately, System.in reads just one key press at a time
and not an entire line. (For example, when the user types 4 then 6 then newline,
this constitutes three key presses.) So, we must compose System.in with additional
objects to read one full line of text:

• System.in is composed with an InputStreamReader:

new InputStreamReader(System.in)

• Next, the above object is composed with a BufferedReader object:

new BufferedReader(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,

System.out.print("Type a dollars amount: ");


String dollars = keyboard.readLine();
System.out.print("Type a cents amount: ");
String cents = keyboard.readLine();
11.4. CASE STUDY: PAYROLL PROCESSING 687

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));

System.out.print("Type a dollars amount: "); // print a partial line


String dollars = keyboard.readLine(); // read one full line of typed input
System.out.print("Type a cents amount: "); // do it again....
String cents = keyboard.readLine();
int money = (100 * new Integer(dollars).intValue())
+ new Integer(cents).intValue();
// the remainder of the program stays the same:
System.out.println( "quarters = " + (money/25) );
money = money % 25;
System.out.println( "dimes = " + (money/10) );
money = money % 10;
System.out.println( "nickels = " + (money/5) );
money = money % 5;
System.out.println( "pennies = " + money );
}
}

11.4 Case Study: Payroll Processing


String tokenizers and disk file objects can help create input views that hide all details
about file processing from the other components of an application. A standard exam-
ple is payroll processing: Perhaps a company keeps its weekly payroll as a sequential
file where each line contains an employee’s name, hours worked, and hourly payrate,
formatted as follows:

Fred Mertz|31|20.25
Lucy Ricardo|42|24.50
Ethel Mertz|18|18.00
!
688

Figure 11.4: input view interface

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.

The sequence of lines is terminated by a special symbol, say, !. A payroll application


reads the lines one by one, extracts the tokens from each line, converts them to names
and numbers, and calculates paychecks. The effort spent on extracting names and
numbers is best placed into a class that acts as the application’s input view.
For the payroll application, the input view’s responsibilites are to extract a name,
hours worked, and payrate from each input record (line). Table 4 shows the interface
for the payroll application’s input view.
We can write an interface for the payroll application’s output view as well, which
will print the paychecks that are calculated. (Call the output view class PayrollWriter.)
Based on these interfaces, we might write the controller for the payroll application
that appears in Figure 5. The controller need not worry about the exact format of
the records in the input payroll file, and it need not worry about how paychecks are
nicely printed—these responsibilities are handled by the view objects.
Figure 6 presents one possible coding of the input view, class PayrollReader.
The hard work is performed within getNextRecord, which verifies the input file is
nonempty before it reads the next record. The method also verifies that the record
is not the end-of-file marker, !, and that the record indeed has a string name, an
integer hours worked, and a double payrate. If all these conditions hold true, then
the information from the record is saved, and true is returned. The method skips
over badly formatted records and returns false when the end of the input is reached.
An exception handler recovers from exceptions that arise when intValue and
doubleValue fail. Because we are forced to write a handler for these RuntimeExceptions,
we use that handler to catch a throw new RuntimeException(line), which announces
an incorrect quantity of data on an input line. Since getNextRecord must also cope
with a possible IOException, we attach two exception handlers to the same try clause.
11.4. CASE STUDY: PAYROLL PROCESSING 689

Figure 11.5: controller for payroll application


/** Payroll prints a file of paychecks from an input payroll file. */
public class Payroll
{ public static void main(String[] args)
{ DialogReader starter = new DialogReader(); // see Fig. 14, Ch. 9
String in name = starter.readString("Please type input payroll name:");
String out name = starter.readString("Please type output payroll name:");
if ( in name != null && out name != null )
{ processPayroll(in name, out name); }
System.out.println("finished");
}

private static void processPayroll(String in, String out)


{ PayrollReader reader = new PayrollReader(in);
PayrollWriter writer = new PayrollWriter(out);
while ( reader.getNextRecord() )
{ double pay = reader.hoursOf() * reader.payrateOf();
writer.printCheck(reader.nameOf(), pay);
}
reader.close();
writer.close();
}
}
690

Figure 11.6: input-view class for payroll processing


import java.io.*;
import java.util.*;
/** PayrollReader reads records from a sequential file. The records have
* the format, NAME|HOURS|PAYRATE. The file is terminated by a ! */
public class PayrollReader
{ private BufferedReader infile; // the address of the input file
private String END OF FILE = "!";
// the name, hours, and payrate of the most recently read record:
private String name;
private int hours;
private double payrate;

/** 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());
}
}

public String nameOf() { return name; }

public int hoursOf() { return hours; }

public double payrateOf() { return payrate; }

public void close()


{ try { infile.close(); }
catch (IOException e)
{ System.out.println("PayrollReader warning: file close failed"); }
}
...
11.4. CASE STUDY: PAYROLL PROCESSING 691

Figure 11.6: input-view class for payroll processing (concl.)


/** getNextRecord attempts to read a new payroll record.
* @return whether another record was read and is ready to process */
public boolean getNextRecord()
{ boolean result = false;
name = null;
hours = -1;
payrate = -0.1;
try { if ( infile.ready() )
{ String line = infile.readLine();
StringTokenizer t = new StringTokenizer(line, "|");
String s = t.nextToken().trim();
if ( ! s.equals(END OF FILE) ) // finished?
{ if ( t.countTokens() == 2 ) // hours and payrate?
{ name = s;
hours = new Integer
(t.nextToken().trim()).intValue();
payrate = new Double
(t.nextToken().trim()).doubleValue();
result = true;
}
else { throw new RuntimeException(line); }
}
}
}
catch (IOException e)
{ System.out.println("PayrollReader error: " + e.getMessage()); }
catch (RuntimeException e)
{ System.out.println("PayrollReader error: bad record format: "
+ e.getMessage() + " Skipping record");
result = getNextRecord(); // try again
}
return result;
}
}
692

These techniques with exceptions are examined in the sections that follow.

Exercise
Specify and write class PayrollWriter for the payroll example.

11.5 Exceptions and Exception Handlers


Even though it might be well written, a program can receive bad input data, for
example, a sequence of letters when a number is required. When this has happened
with earlier examples, the program stopped in the middle of its execution and an-
nounced that an error (exception) occurred. How can a program protect itself from
this occurrence?
The Java language provides a construction, called an exception handler, that pro-
vides protection. Here is an example: A program must compute a division with an
integer that is supplied as input:
import javax.swing.*;
/** DivideIntoTwelve reads an int and divides it into 12 */
public class DivideIntoTwelve
{ public static void main(String[] args)
{ int i = readAnInt();
JOptionPane.showMessageDialog(null, "Answer is " + (12 / i));
System.out.println("Finished.");
}

/** readAnInt reads an int and returns it */


private static int readAnInt()
{ String s = JOptionPane.showInputDialog("Please type an int:");
int num = new Integer(s).intValue();
return num;
}
}

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:

Type an int: one


Exception in thread "main" java.lang.NumberFormatException: one
11.5. EXCEPTIONS AND EXCEPTION HANDLERS 693

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)

The message documents the methods incompleted due to the exception.


Both errors are examples of runtime exceptions, so named because they occur
when the program is “running.” The wordy messages produced from exceptions are
confusing to the user, and an application can do better about reporting the exceptions.
We improve the program by inserting exception handlers. An exception handler
is a kind of protective “cover” that surrounds a questionable statement and helps the
program recover from exceptions generated by the statement.
To help the program recover from division-by-zero errors, we surround the state-
ment with the division operation with an exception handler (note the keywords, try
and catch):

public static void main(String[] args)


{ int i = readAnInt();
try { JOptionPane.showMessageDialog(null, "Answer is " + (12 / i)); }
catch(RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"Error in input: " + i + ". Restart program.");
}
System.out.println("Finished.");
}

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.

11.5.1 Restarting a Method with an Exception Handler


The example in the previous section used a private method, readAnInt, to read an
integer from its user. What if the user types letters instead of an integer, e.g., four
instead of 4? To handle this situation, readAnInt can employ an exception handler
that gives the application’s user multiple tries at typing a proper integer—if the user
types an improper input, the exception handler’s catch-section restarts readAnInt by
sending a message to the method itself:
/** readAnInt reads an int and returns it */
private static int readAnInt()
{ int num;
String s = JOptionPane.showInputDialog("Please type an int:");
try { num = new Integer(s).intValue(); }
catch(RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"Input " + s + " not an int; try again!");
num = readAnInt(); // restart with a recursive invocation!
}
return num;
}

When a non-integer is supplied as input, a runtime exception is thrown at new


Integer(s), and the catch section goes to work: a dialog appears, then a message is
sent to the method to restart itself, giving the user a new chance to type an integer.
When a method restarts itself, it is called a recursive invocation.
11.5. EXCEPTIONS AND EXCEPTION HANDLERS 695

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"

try{ 2> num = new Integer(s).intValue(); }


catch( ... ) { ... }
return num;
}

The attempted conversion of four to an integer throws a runtime exception, and


execution moves into the catch section:

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

restarted method proceeds without exception:


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
{ 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

Figure 11.7: interface for input views

This is a standardized interface for a class


that reads interactive input.
Methods (responsibilities)
readString(String prompt): String prompt the user for input with prompt,
read a string, and return it as the result
readInt(String prompt): int prompt the user for input with prompt,
read an integer, and return it as the result
readDouble(String prompt): double prompt the user for input with prompt,
read a double, and return it as the result

will use recursive invocations to restart methods; Chapters 7 and 12 present other
uses for recursive invocations.

11.5.2 Interactive Input with Exception Handlers


Whenever we required interactive input, we wrote a sequence of statements like the
following:

String input = JOptionPane.showInputDialog("Type an integer:");


int value = new Integer(input).intValue();

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:

DialogReader reader = new DialogReader();


int value = reader.readInt("Type an integer:");

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:

public static void main(String[] args)


{ DialogReader reader = new DialogReader(); // the new input-view
int c = reader.readInt("Type an integer Celsius temperature:");
// this remains unchanged:
double f = celsiusIntoFahrenheit(c);
698

DecimalFormat formatter = new DecimalFormat("0.0");


System.out.println("For Celsius degrees " + c + ",");
System.out.println("Degrees Fahrenheit = " + formatter.format(f));
}

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:

answer = new Integer(s.trim()).intValue();

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.

11.6 Exceptions Are Objects


We have seen how exceptions can terminate a program’s execution and how an excep-
tion handler can trap an exception so that execution can resume. Now, we learn that
exceptions are objects that bind to the formal parameters of of exception handlers.
When an error (such as division by zero) occurs, the program must collect infor-
mation about the error and deliver the information to a handler of the exception.
The information collected about the error usually comes in two parts:

• a description of the nature of the error (e.g., “division by zero”);

• 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

Figure 11.8: class for input via a dialog


import javax.swing.*;
/** DialogReader accepts user input from a dialog */
public class DialogReader
{ /** Constructor DialogReader initializes the input view, a dialog */
public DialogReader() { } // nothing to initialize

/** readString reads a string from the input source.


* @param prompt - the prompt that prompts the user for input
* @return the string the user types in response */
public String readString(String prompt)
{ return JOptionPane.showInputDialog(prompt); }

/** readInt reads an integer from the input source


* @param prompt - the prompt that prompts the user for input
* @return the integer supplied in response */
public int readInt(String prompt)
{ int answer = 0;
String s = readString(prompt);
try { answer = new Integer(s.trim()).intValue(); }
catch (RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"DialogReader error: " + s + " not an int.");
answer = readInt(prompt); // restart
}
return answer;
}

/** readDouble reads a double from the input source


* @param prompt - the prompt that prompts the user for input
* @return the double supplied in response */
public double readDouble(String prompt)
{ double answer = 0;
String s = readString(prompt);
try { answer = new Double(s.trim()).doubleValue(); }
catch (RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"DialogReader error: " + s + " not a double.");
answer = readDouble(prompt); // restart
}
return answer;
}
}
700

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 }

It defines a method, catch, that is “invoked” within STATEMENTS if an exception occurs


there. If an exception object is created within STATEMENTS, and if the exception’s type
is a subtype of EXCEPTION TYPE, then the exception object binds to formal parameter
e, and HANDLER executes. (If the exception’s type is not a subtype of EXCEPTION CLASS,
then the exception “escapes” to find its handler.) Once HANDLER finishes, execution
continues with the statements that follow it. Within HANDLER, messages can be sent
to the exception object, e.g., e.printStackTrace().
If a programmer writes an exception handler, she is obligated to make the han-
dler repair the error to a degree that execution can safely proceed. In this regard,
programmer-written exception handlers can prove dangerous, because it is all too
easy for a programmer to do too little to repair an error.
Here is a simple example. Perhaps we must write a method, readAnIntFrom, which
reads a string from the input-view object and converts it into a string. An exception
handler is added to cope with the possibility that the input string is not an integer:
/** readAnIntFrom reads an int from input-view, view, and returns it */
private int readAnIntFrom(BufferedReader view) throws IOException
{ int num;
System.out.print("Type an int: ");
11.6. EXCEPTIONS ARE OBJECTS 701

Figure 11.9: classes of exceptions


Object
|
+-Throwable: getMessage():String, toString():String, printStackTrace()
|
+-Exception
|
+-InterruptedException
|
+-RuntimeException
| |
| +-ArithmeticException
| |
| +-ArrayIndexOutOfBoundsException
| |
| +-ClassCastException
| |
| +-NullPointerException
| |
| +-NumberFormatException
| |
| +-StringIndexOutOfBoundsException
| |
| +-NoSuchElementException
|
+-IOException
|
+-FileNotFoundException
|
+-EOFException
|
+-InterruptedIOException
702

Figure 11.10: methods for exception objects

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;
}

If the string returned by readLine is nonnumeric, then a runtime exception arises


at new Integer(s).intValue(). A NumberFormatException object is created and is
promptly caught by the enclosing catch, which merely prints a message and lets the
method return -1 as its result. Since -1 might be confused for a legitimate integer
input, the exception handler has not done a good enough job. We improve it as seen
in Figure 11.
In the Figure, the handler insists that the user type another string, and it invokes
itself to restart the process. This ensures that the method concludes only when a
proper integer has been typed. The message, e.getMessage(), extracts information
about the nature of the error. Finally, the exception handler is refined to handle only
NumberFormatExceptions, which ensures that the handler is used only for the form of
error it is prepared to handle, namely, a format error for a string that is nonnumeric.
When an exception is thrown, it is not always obvious which catch-construct will
handle it. If an exception is thrown within a method, and if the method does not
catch the exception, then the exception seeks its handler by returning to the method
that invoked the erroneous method and searching there.
The following artificial example explores this idea. Perhaps we have

ExceptionExample ex = new ExceptionExample();


ex.f();

where class ExceptionExample is defined as


11.6. EXCEPTIONS ARE OBJECTS 703

Figure 11.11: improved exception handling for reading integers


/** readAnIntFrom reads an integer from the input source
* @param view - the input-view object
* @return the integer supplied in response. (And keep trying until the user
* supplies a legal integer!) */
private int readAnIntFrom(BufferedReader view) throws IOException
{ int num;
System.out.print("Type an int: ");
String s = view.readLine();
try { num = new Integer(s).intValue(); }
catch(NumberFormatException e)
{ JOptionPane.showMessageDialog(null, "Error: " + e.getMessage()
+ " not an integer; try again.");
num = readAnIntFrom(view); // restart
}
return num;
}

import java.io.*;
public class ExceptionExample
{ public ExceptionExample() { }

public void f()


{ try { g(); }
catch (RuntimeException e) { System.out.println("caught at f"); }
System.out.println("f completes");
}

public void g()


{ try { PrintWriter outfile = new PrintWriter(new FileWriter("text.out"));
try { outfile.println( h() ); }
catch (NullPointerException e)
{System.out.println("null pointer caught at g"); }
}
catch (IOException e)
{ System.out.println("io error caught at g"); }
System.out.println("g completes");
}

private int h()


{ int[] r = new int[2];
return r[3];
704

}
}

The message to f causes a message to g which causes a message to h, where an


array indexing error occurs. Within h, an ArrayIndexOutOfBoundsException ob-
ject is created, and the plan of returning an integer from h is abandoned. In-
stead, the exception object seeks its handler. No handler appears in h, so the ex-
ception returns to the position where h was invoked. This places it at the state-
ment, outfile.println( h() ) within g. The plan to perform the outfile.println
is abandoned; the exception seeks its handler instead. It examines the data type,
NullPointerException, of the nearest enclosing handler, but the type does is not a
subtype of ArrayIndexOutOfBoundsException, so the exception object searches fur-
ther. The next enclosing handler has type IOException, but this is also unacceptable,
so the exception returns from g to the position, g(), within method f.
Here, the exception finds a handler that can accommodate it: The exception’s
type is a subtype of RuntimeException, so the message, caught at f, is printed. The
handler consumes the exception, and normal execution resumes at the statement,
System.out.println("f completes"), which prints. Execution concludes at ex.f(),
which is unaware of the problems that occurred.
The above example was meant to illustrate a technical point; it is not an example of
good programming style. Indeed, exception handlers are best used to help a program
recover from actions over which it has no influence, e.g., a user typing invalid input.
Exception handlers are not so useful for detecting internal programming errors such
as array indexing errors. As a rule,

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 705

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.

2. Revise class CopyFile in Figure 3 so that an exception handler catches the


FileNotFoundException that might arise when a user supplies an invalid name
for the input file. The exception handler tells the user of the error and asks her
to type a new file name.

11.6.1 Programmer-Generated Exceptions


In the rare case, it is possible for a programmer to escape from a disastrous situation
by constructing an exception object:
try { ... // perhaps some complicated computation is undertaken
if ( some_bad_condition_is_true )
{ throw new RuntimeException("terrible error"); }
... // otherwise, continue with the complicated computation
}
catch (RuntimeException e)
{ ... e.getMessage() ... } // try to handle the bad condition

The statement, throw new RuntimeException(S), constructs a runtime exception ob-


ject and “throws” to its handler. String S is the message embedded within the ex-
ception and is the answer produced by a getMessage.
If one wants to be certain that the explicitly generated exception is not inadver-
tantly confused with the other forms of runtime exceptions, one can create a new
class for the new exception, e.g.,
706

public class ComplicatedException extends Exception


{ private int villain; // the integer that caused all the trouble

public ComplicatedException(String error_message, int the_bad_number)


{ super(error_message);
villain = the_bad_number;
}

public int getVillain()


{ return villain; }
}

Here, class ComplicatedException is a subclass of Exception, meaning that its ob-


jects can be “thrown.” Its constructor method has been enhanced to accept both an
error message and an integer, which might be useful to its exception handler. The
new class of exception might be used as follows:

try { int x = ... ;


... // some complicated computation is undertaken
if ( some_bad_condition_is_true_about(x) )
{ throw new ComplicatedException("terrible error", x); }
... // otherwise, continue with the complicated computation
}
catch (ComplicatedException e)
{ ... e.getMessage() ...
int i = e.getVillain();
...
}

The statement, throw new ComplicatedException("terrible error", x), builds a


ComplicatedException object and throws it to its handler, which sends messages
to the exception to learn the nature of the error.

11.7 Summary
We summarize the main points of the chapter.

New Construction
• exception handler:

public int readInt(String prompt)


{ int answer = 0;
String s = readString(prompt);
11.7. SUMMARY 707

try { answer = new Integer(s.trim()).intValue(); }


catch (RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"DialogReader error: " + s + " not an int.");
answer = readInt(prompt); // restart
}
return answer;
}

New Terminology
• token: a “word” within a string or a line of text

• delimiter: a character that is used to separate tokens in a string; examples are


spaces and punctuation.

• file: a collection of symbols stored together on a disk

• 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

1. a character file, which is a sequence of keyboard symbols, saved in the file


as a sequence of bytes;
2. a binary file, which is a sequence of ones-and-zeros.

• 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:

• In Java, an output file of characters is most simply created from a PrintWriter


object, e.g.,

PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));


708

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.,

BufferedReader infile = new BufferedReader(new FileReader("test.txt"));

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.

New Classes for Future Use


• class StringTokenizer: used to disassemble a string into its constituent tokens;
see Table 1.
• class FileWriter: used to construct a connection to an output sequential file;
see Figure 2.
• class PrintWriter: used to write strings to an output sequential file; see Figure
2.
• class FileReader: used to construct a connection to an input sequential file;
see Figure 3.
• class BufferedReader: used to read lines from an input output sequential file;
see Figure 3.
• System.in: a preexisting object that can read symbols typed into the command
window.
• class InputStreamReader: used to connect to System.in.

11.8 Programming Projects


1. Write a text formatting program.
(a) The first version, PrettyPrint, reads a nonempty sequential file, and pro-
duces a sequential file whose lines are “pretty printed,” that is, words are
separated by exactly one blank and every input line has a length that is
as close as possible, but does not exceed, 80 characters. (To simplify this
Project, assume that no single word has a length that exceeds 80 charac-
ters.)
11.8. PROGRAMMING PROJECTS 709

(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).)

5. Write an application that counts occurrences of words in a sequential file:


(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: **
...

(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.

7. Write a program that does simplistic text compression:

(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.

(Assume that \ does not appear in the input file.)


(b) Modify the program so that repetitions of length 10 or greater are com-
pressed as well.
(c) Write the corresponding uncompression program.

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:

• a numeral, n, translates to two integers, 0 and the integer n.


• a left parenthesis, (, translates to the integer, 1, and a right parenthesis,
), translates to the integer, 2.
• the operators, +, -, *, ==, <, <=, &&, and ! translate to the integers 3
through 10, respectively.

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

Figure 11.12: copying a file character by character


import java.io.*;
/** CharCopy copies in.dat to out.dat character by character */
public class CharCopy
{ public static void main(String[] args) throws IOException
{ FileReader infile = new FileReader("in.dat");
FileWriter outfile = new FileWriter("out.dat");
while ( infile.ready() )
{ int c = infile.read(); // reads (the coding of) a character
outfile.write(c); // writes the character
}
infile.close();
outfile.close();
}
}

11.9 Beyond the Basics


11.9.1 Character-by-Character File Processing

11.9.2 Binary Files and Files of Objects

11.9.3 A Taxonomy of File Classes

11.9.4 A GUI for File Selection

Here are some optional topics related to file processing.

11.9.1 Character-by-Character File Processing


It is tedious but nonetheless possible to process sequential files one character at a
time. Figure 12 shows an application that copies the contents of a input file to an
output file this way.
For reasons explained at the end of the chapter, it suffices to work with a FileReader
object to read character input and a FileWriter object to write character output.
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:
712

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.

11.9.2 Binary Files and Files of Objects


When you state,
double d = 1000007.5;
System.out.println(d);

A binary representation of one-million-seven-and-a-half is saved in a single cell, and


this representation is fetched and coverted into a sequence of nine characters for
display on the console window. Conversions of numbers to characters are time con-
suming, and it is best to avoid them.
If a huge array of numbers must be saved in a file for later computation, it
is best to use a binary file. Java provides class ObjectOutputStream and class
ObjectInputStream for writing and reading binary files of numbers, booleans, and ar-
bitrary objects. The values are copied in their binary, storage representations; there
is no conversion to character format.
For example, to write the above number to a binary file, data.out, we create an
output-file object and send a writeDouble message to it:
double d = 1000007.5;
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream("data.out"));
out.writeDouble(d);

Similarly, to read a complete file, data.in, of doubles, we would state


ObjectInputStream in = new ObjectInputStream
(new FileInputStream("data.in"));
while ( in.available() > 0 ) // more data left to read?
{ double d = in.readDouble();
...
}
in.close();

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

Figure 11.13: writing and reading objects from a file


import java.io.*;
/** ObjectFileTest writes and reads two objects and an integer */
public class ObjectFileTest
{ public static void main(String[] args)
throws IOException, ClassNotFoundException
{ Cell b = new Cell(5);
ComposedCell c = new ComposedCell(b);
Cell d = new Cell(7);
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream("test.ob"));
out.writeObject(c);
out.writeInt(49);
out.writeObject(d);
out.close();
// now, read the objects and the integer and restore them:
ObjectInputStream in = new ObjectInputStream
(new FileInputStream("test.ob"));
Object ob = in.readObject();
ComposedCell m = (ComposedCell)ob; // cast ob into a ComposedCell
System.out.println(m.answerOf());
System.out.println(in.readInt());
ob = in.readObject();
Cell n = (Cell)ob; // cast ob into a Cell
System.out.println(n.valueOf());
in.close();
}
}

/** ComposedCell builds an object that holds within it two objects */


public class ComposedCell implements Serializable
{ private Cell x;
private Cell y;

public ComposedCell(Cell my cell)


{ x = my cell;
y = new Cell(0);
}

public int answerOf()


{ return x.valueOf() + y.valueOf(); }
}
714

Figure 11.13: writing and reading objects from a file (concl.)


/** Cell holds an integer */
public class Cell implements Serializable
{ private int value;

public Cell(int start) { value = start; }


public int valueOf() { return value; }
}

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.

11.9.3 A Taxonomy of File Classes


The java.io package holds a variety of classes for input and output; Figure 13 lists
those classes used in this text. Because of a history of alterations to the package,
the intentions behind the class hierarchy are less clear than they might be. When
studying the hierarchy, keep these ideas in mind:

• In Java, data are organized as sequences or streams of bytes. An input stream


can be read; an output stream is written.
11.9. BEYOND THE BASICS 715

Figure 11.14: class hierarchy for input-output


Object
|
+-File: File(String), getPath():String, isFile():boolean, isDirectory():Boolean
|
+-InputStream (abstract): available():int, close()
| |
| +-FileInputStream: FileInputStream(String), FileInputStream(File)
| | read():int
| |
| +-FilterInputStream: read():int
| |
| +-ObjectInputStream: ObjectInputStream(InputStream), read():int,
| readInt():int, readDouble():double, readBoolean():boolean,
| readObject():Object
|
+-OutputStream (abstract): close()
| |
| +-FileOutputStream: FileOutputStream(String), FileOutputStream(File),
| | write(int)
| |
| +-FilterOutputStream: write(int)
| |
| +-ObjectOutputStream: ObjectOutputStream(OutputStream), write(int),
| writeInt(int), writeDouble(double), writeBoolean(boolean),
| writeObject(Object)
|
+-Reader (abstract)
| |
| +-InputStreamReader: InputStreamReader(InputStream), read():int,
| | | ready():boolean, close()
| | |
| | +-FileReader: FileReader(String)
| |
| +-BufferedReader: BufferedReader(Reader), read():int, ready():boolean,
| readLine():String, close()
|
+-Writer (abstract)
|
+-OutputStreamWriter: OutputStreamWriter(OutputStream), write(int), close()
| |
| +-FileWriter: FileWriter(String)
|
+-PrintWriter: PrintWriter(Writer), PrintWriter(OutputStream), write(int),
print(int), print(double), print(boolean), print(Object),
println(int), println(double), println(boolean), println(Object),
println(), close()
716

• 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"));

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.
11.9. BEYOND THE BASICS 717

class File contains the path name and basic properties of


a file
Constructor
File(String path) Constructs the file object from the complete
path name, path.
Methods
getPath(): String Return the complete directory path name of the
file object.
isFile(): boolean Return whether the file object is a file.
isDirectory(): boolean Return whether the file object is a directory.

abstract class InputStream a sequence (“stream”) of data items that can


be read one byte at a time
Methods
available(): int Return how many bytes are available for read-
ing.
close() Close the file.

class FileInputStream a stream that is a sequential disk file


extends InputStream
Constructors
FileInputStream(String path) Open the disk file whose path name is path.
FileInputStream(File f) Open the disk file named by f.
Method
read(): int Read the next unread byte from the file and
return it; return -1 if the file is at the end.

class FilterInputStream a basic input stream, e.g., from the keyboard.


extends InputStream
Method
read(): int Return the next byte from the stream; return
-1 if the end of the stream is reached.
718

class ObjectInputStream an input stream from which binary values


extends InputStream (primitive values and objects) can be read
Constructor
ObjectInputStream(InputStream Uses stream to construct an object-input
stream) stream
Methods
read(): int Return the next byte from the stream; return
-1 if the end of the stream is reached.
readInt(): int Return a 32-bit integer assembled from bytes
read from the stream; throws EOFException if
end of the stream is reached.
readDouble(): double, Similar to readInt().
readBoolean(): boolean
readObject(): Object Return an object assembled from bytes read
from the stream; throws an exception if a com-
plete object cannot be assembled.

abstract class OutputStream a sequence (“stream”) of data items that have


been written
Method
close() Close the file.

class FileOutputStream an output stream that is a sequential disk file


Constructors
FileOutputStream(String Open the disk file whose path name is path.
path)
FileOutputStream(File f) Open the disk file named by f.
Methods
write(int b) Write byte b to the file.
close() Close the file.

class FilterOutputStream a basic output stream, e.g., to the console win-


extends OutputStream dow.
Method
write(int b) Write byte b to the output stream.
11.9. BEYOND THE BASICS 719

class ObjectOutputStream an output stream to which binary values (prim-


extends OutputStream itive values and objects) can be written
Constructor
ObjectOutputStream(OutputStreamUses stream to construct an object-output
stream) stream
Methods
write(int b) Write byte b to the stream.
writeInt(int i) Writes i, a 32-bit integer, to the stream.
writeDouble(double d), Similar to writeInt().
writeBoolean(boolean b)
writeObject(Object ob) Writes ob to the output stream, provided
that the class from which ob was constructed
implements Serializable; throws an excep-
tion, otherwise.

abstract class Reader a sequence of characters that can be read

class InputStreamReader a basic character stream


extends Reader
Constructor
InputStreamReader(InputStream Construct a character stream that reads bytes
stream) from stream to assemble its characters.
Methods
read(): int Read the next unread character from the stream
and return it as an integer; return -1 if the end
of the stream is reached.
ready(): boolean Return whether the stream contains more char-
acters to be read.
close() Close the stream.

class FileReader extends an InputStreamReader constructed from a


Reader FileInputStream.
Constructors
FileReader(String s), Constructs the input-stream reader—an ab-
FileReader(File s), breviation for new InputStreamReader(new
FileInputStream(s)).
720

class BufferedReader extends a character stream from which complete lines of


Reader text can be read
Constructor
BufferedReader(Reader r) Construct a buffered reader that reads its char-
acters from r.
Methods
read(): int Read the next unread character from the stream
and return it as an integer; return -1 if the end
of the stream is reached.
readLine(): String Read a line of text from the stream, that is, a
maximal sequence of characters terminated by
a newline (’\n’) or return-newline (’\r’ and
’\n’). Return the line less its terminator char-
acters; return null if the end of the stream is
reached.
ready(): boolean Return whether the stream contains more char-
acters to be read.
close() Close the stream.

abstract class Writer a writable sequence of characters

class OutputStreamWriter a basic character stream


extends Writer
Constructor
OutputStreamWriter(OutputStreamConstruct a character stream to which charac-
stream) ters can be written by means of stream.
Methods
write(int c) Write character c to the stream.
close() Close the stream.

class FileWriter extends an OutputStreamWriter constructed from a


Writer FileOutputStream
Constructors
FileWriter(String s), Constructs the output-stream writer—an
FileWriter(File s), abbreviation for new OutputStreamWriter(new
FileOutputStream(s)).
11.9. BEYOND THE BASICS 721

class PrintWriter extends a buffered character stream to which primi-


Writer tive values and objects can be written (in their
string depictions)
Constructors
PrintWriter(OutputStream s), Construct a print writer that sends its values as
PrintWriter(Writer s) character sequences to s.
Methods
write(int c) Write character c to the output stream.
print(int d), print(double Write the character representation of d to the
d), print(boolean d), output stream. (When d is an object, d’s
print(Object d) toString method is used to get its representa-
tion.)
println(int d), Like print but appends line termination char-
println(double d), acter(s).
println(boolean d),
println(Object d) println()
close() Close the stream.
722

11.9.4 A GUI for File Selection


The javax.swing package contains a component from which you can create a dialog
that displays a folder (directory); the object, called a file chooser, looks like this:

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

Figure 11.15: methods for JFileChooser

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

showDialog(Component Display the file chooser as a dialog, using owner as


owner, String the dialog’s owner component. The approval but-
text for approve button) ton is labelled with text for approve button.

{ 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

Text and File Processing


11.1 Strings are Immutable Objects
11.1.1 String Tokenizers
11.2 Sequential Files
11.2.1 Output to Sequential Files
11.2.2 Input from Sequential Files
11.3 Sequential Input from the Command Window
11.4 Case Study: Payroll Processing
11.5 Exceptions and Exception Handlers
11.5.1 Restarting a Method with an Exception Handler
11.5.2 Interactive Input with Exception Handlers
11.6 Exceptions Are Objects
11.6.1 Programmer-Generated Exceptions
11.7 Summary
11.8 Programming Projects
11.9 Beyond the Basics
The applications presented in previous chapters accepted textual input by means of
the user typing it into a window, one data item per line. Output was displayed in a
similar way. Window-based input and output are inadequate for programs that must
create and maintain large amounts of textual data, where multiple data items reside
in each textual line and multiple lines of text reside in disk files. In this chapter, we
study the following techniques:
• how to decompose a line of text that contains multiple data items into its con-
stituent parts, called tokens, by means of string tokenizers;
• how to read and write lines of text to and from sequential disk files;
• how to adapt the same techniques to read lines of text from the command win-
dow;
• how to use exception handlers to deal with errors in file processing.
Because we work with files of text strings, we first examine how strings are represented
in computer storage.
11.1. STRINGS ARE IMMUTABLE OBJECTS 681

11.1 Strings are Immutable Objects


Since Chapter 2, we have worked with strings, which are represented in Java as
sequences of characters enclosed by double quotes, e.g., "abcd". Special characters,
like backspaces, tabs, and newlines, can be encoded within a string with these codings:
• \b (backspace)
• \t (tab)
• \n (newline)
• \r (return)
• \" (doublequote)
• \’ (single quote)
• \\ (backslash)
For example, System.out.println("a\tb\nc\"d") would print as
a b
c"d
In previous chapters, we have systematically ignored the fact that string are ob-
jects in Java; there was no need to worry about this point. For the curious, we now
reveal that a string is in fact saved in computer storage as an array of characters.
The initialization,
String s = "abcd";
creates this object in storage:
a1 : String
String s == a1
’a’ ’b’ ’c’ ’d’

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

char[] r = new char[2];


r[0] = ’a’;
r[1] = ’b’;
String u = new String(r);
String v = "ab";

The above statements create the string, "ab", and assign its address to t:

char[ ] r == a2 String u == a3 String v == a4

a2 : char[2] a3 : String a4 : String

’a’ ’b’ ’a’ ’b’ ’a’ ’b’

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.

Unlike an array of characters, you cannot assign to a character (element) of a string.


And when we write, say, String u = s + "e", this creates a new string object; it does
not alter the string denoted by s:

String s == a1 String u == a6

a1 : String a5 : String a6 : String

’a’ ’b’ ’c’ ’d’ ’e’ ’a’ ’b’ ’c’ ’d’ ’e’

At this point, if we assigned,


s = u;

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:

1. public boolean stringEquals(String s, String t) returns true is strings s


and t hold exactly the same sequences of characters and returns false otherwise.

2. public String extractSubstring(String s, int start, int end) returns the


string within s that begins at index start and ends at index end - 1. If such
a substring does not exist, because either of start or end has an invalid value,
then null is returned.

3. public String trimBlanks(String s) returns a string that looks like s but all
leading blanks and all trailing blanks are removed.

11.1.1 String Tokenizers


Our applications that read input have been designed to read one word—one token—
per input line. But it is more convenient for a programmer to type multiple tokens on
the same line. For example, a person would prefer to type her first, middle, and last
names on one line rather than on three. And a user of the change-making program in
Figure 2, Chapter 4, would prefer to type $13.46 as the program’s input rather than
13 on one line and 46 on another.
Of course, we can write an auxiliary method that decomposes a string into its
tokens, but this task arises often, so the Java designers wrote a helper class, class
StringTokenizer (found in the package, java.util), that contains methods for de-
composing strings into tokens.
Perhaps we have a string, s, that contains several tokens that are separated by spe-
cial characters, called delimiters. For example, a string, Lucy MacGillicutty Ricardo,
contains the tokens Lucy, MacGillicutty, and Ricardo, separated by blank spaces; the
blank serves as the delimiter. We construct a string tokenizer object that extracts
the tokens as follows:
684

String s = "Lucy MacGillicutty Ricardo";


StringTokenizer t = new StringTokenizer(s, " ");

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"

(Note: strings will no longer be presented text == "Lucy MacGillicutty Ricardo"


as arrays; we understand this implicitly)
" "
delimiters ==
StringTokenizer t == a1

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"

text == " MacGillicutty Ricardo"


StringTokenizer t == a1
" "
delimiters ==
String first name == "Lucy"

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

Figure 11.1: string tokenizer methods

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

int i = new Integer( y.nextToken() ).intValue()


+ new Integer( y.nextToken() ).intValue();
System.out.println(s + i);
(b) String x = "12!3,4";
StringTokenizer y = new StringTokenizer(x, ",");
String s = y.nextToken();
int i = new Integer( y.nextToken() ).intValue();
StringTokenizer t = new StringTokenizer(s,"!");
String a = t.nextToken();
int j = new Integer( t.nextToken() ).intValue();
System.out.println(i+j);

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.

11.2 Sequential Files


Computer programs can read input from and write output to disk files. Simply stated,
a file is a sequence of symbols stored on a diskette or on a computer’s internal disk.
There are two formats of files:

• a character file, which is a sequence of keyboard symbols, saved in the file as a


sequence of bytes;

• a binary file, which is a sequence of ones-and-zeros.

A file is indeed one long sequence; for example, these three text lines,

Hello to you!
How are you?
49

would be stored as a character file as this long sequence:

Hello to you!\nHow are you?\n 49\n


11.2. SEQUENTIAL FILES 687

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.,

Hello to you!\r\nHow are you?\r\n 49\r\n

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

• output: the file is written sequentially and cannot be read.


It is possible for a program to open a sequential file, read from it, close it, and then
reopen it and write to it. If one desires a file that one can open and both read and
write at will, then one must use a random-access file.
From this point onwards, we use the term sequential file to mean a sequential file
of (codings of) characters. We will not deal with binary files in this text.

11.2.1 Output to Sequential Files


The System.out object used methods named print and println to write data to the
console. We do something similar with sequential files.
The package, java.io, contains classes that have methods for file input and output.
To write to a sequential character file, we construct an object that connects to the
file and deposits text into it; say that the file that will hold the output is named
test.txt:

1. First, we construct the object,

Filewriter w = new FileWriter("test.txt");

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:

PrintWriter outfile = new PrintWriter(w);

The PrintWriter object accepts print(E) and println(E) messages, where it


translates the argument, E, into a sequence of characters and sends the charac-
ters, one by one, to the FileWriter object, w. For example,

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 11.2: sequential file output


import java.io.*;
/** Output1 writes three lines of characters to file test.txt */
public class Output1
{ public static void main(String[] args) throws IOException
{ PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));
outfile.println("Hello to you!");
outfile.print("How are");
outfile.println(" you?");
outfile.println(47+2);
outfile.close();
}
}

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 former contents of test.txt, if any, were destroyed.


The program begins with import java.io.*, which is the package where the new
classes live, and main’s header line states throws IOException, which the Java com-
piler requires whenever file input/output appears. The phrase is a warning that that
output transmission via might lead to an error (exception), e.g., the disk drive fails
in the middle of output transmission. IOExceptions can be processed with exception
690

handlers, which we first studied in Chapter 4. We employ exception handlers later in


the chapter.
It is perfectly acceptable to use multiple output files within the same program,
and it is done in the expected way:
PrintWriter file1 = new PrintWriter(new FileWriter("test.txt"));
PrintWriter file2 = new PrintWriter(new FileWriter("AnotherFile.out"));
...
file1.println("abc");
file2.print("c");
file2.println();
file1.close();
...

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 temperature-conversion program in Figure 2, Chapter 4, so that the


program’s output is displayed in a console window and also is written to a file,
change.out.

11.2.2 Input from Sequential Files


For sequential file input, we must again construct an input-view object that connects
to the sequential file that holds the input text. This is again constructed in two
stages:
1. First, we construct an object that holds the file’s physical address and reads
individual characters from the file:

FileReader r = new FileReader("test.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.)

2. Next, we compose the FileReader object with a BufferedReader object, that


has a method that reads one full text line:

BufferedReader infile = new BufferedReader(r);


11.2. SEQUENTIAL FILES 691

Figure 11.3: program that copies a file


import java.io.*;
/** CopyFile copies the contents of an input file, f,
* whose name is supplied by the user, into an output file, f.out */
public class CopyFile
{ public static void main(String[] args) throws IOException
{ String f = JOptionPane.showInputDialog("Input filename, please: ");
// construct the view object that reads from the input file:
BufferedReader infile = new BufferedReader(new FileReader(f));
// construct the view object that writes to the output file:
PrintWriter outfile = new PrintWriter(new FileWriter(f + ".out"));
while ( infile.ready() ) // are there more lines to read in infile?
{ String s = infile.readLine();
outfile.println(s);
}
infile.close();
outfile.close();
}
}

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.

As an example, we write an application that reads the contents of a sequential


file whose name is supplied by the user at the keyboard and copies the file, f, line by
line, into another file, f.out. Figure 3 shows the application, class CopyFile.
The statement, infile = new BufferedReader(new FileReader(f)), creates a BufferedReader
object that remembers the address of the filename, f, supplied by the user. In
a similar fashion, outfile = new PrintWriter(new FileWriter(f + ".out")) creates
an object that knows the address of the output file.
The while-loop within main uses a new method, infile.ready(), to determine if
there are lines of text waiting to be read from infile. If so, then another line is
read by s = infile.readLine() and is written by outfile.println(s). (Note: if the
readLine method attempts to read a string from a file that is exhausted, it returns
null as its result.)
Errors can arise when we use disk files. For example, say that we tell the program
to read a nonexistent file, fred. The program stops at the assignment to infile (Line
10) and announces:

java.io.FileNotFoundException: fred (No such file or directory)


...
692

at java.io.FileReader.<init>(...)
at CopyFile.main(CopyFile.java:10)

The error, java.io.FileNotFoundException, is an example of an IOException—the


program has “thrown” an exception, as was warned in the header line of main. Such
exceptions can be handled by exception handlers, as we see in the examples in the
next section.

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: Write a new input-view class, class
VoteReader, whose readInt method reads a vote from votes.dat.)

11.3 Sequential Input from the Command Win-


dow
Since the beginning of this text, we used System.out.println(E) to display messages,
E, within the command window. It is also possible to read input text that the user
types into the command window—we use a preexisting object, System.in, which
connects to the command window, along with the techniques seen in the previous
section.
Here is an example: Recall that the change-making application, MakeChange,
from Figure 3, Chapter 3, calculates the change one extracts from dollars-and-cents
amounts. We can revise this program so the user starts it first and decides next what
the dollars and cents inputs might be. When the revised application is started, this
11.3. SEQUENTIAL INPUT FROM THE COMMAND WINDOW 693

request appears in the command window:

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:

In Java, System.in is the object that is connected to the computer’s keyboard


for interactive input. Unfortunately, System.in reads just one key press at a time
and not an entire line. (For example, when the user types 4 then 6 then newline,
this constitutes three key presses.) So, we must compose System.in with additional
objects to read one full line of text:

• System.in is composed with an InputStreamReader:

new InputStreamReader(System.in)

• Next, the above object is composed with a BufferedReader object:

new BufferedReader(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,

System.out.print("Type a dollars amount: ");


String dollars = keyboard.readLine();
System.out.print("Type a cents amount: ");
String cents = keyboard.readLine();
11.4. CASE STUDY: PAYROLL PROCESSING 695

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));

System.out.print("Type a dollars amount: "); // print a partial line


String dollars = keyboard.readLine(); // read one full line of typed input
System.out.print("Type a cents amount: "); // do it again....
String cents = keyboard.readLine();
int money = (100 * new Integer(dollars).intValue())
+ new Integer(cents).intValue();
// the remainder of the program stays the same:
System.out.println( "quarters = " + (money/25) );
money = money % 25;
System.out.println( "dimes = " + (money/10) );
money = money % 10;
System.out.println( "nickels = " + (money/5) );
money = money % 5;
System.out.println( "pennies = " + money );
}
}

11.4 Case Study: Payroll Processing


String tokenizers and disk file objects can help create input views that hide all details
about file processing from the other components of an application. A standard exam-
ple is payroll processing: Perhaps a company keeps its weekly payroll as a sequential
file where each line contains an employee’s name, hours worked, and hourly payrate,
formatted as follows:

Fred Mertz|31|20.25
Lucy Ricardo|42|24.50
Ethel Mertz|18|18.00
!
696

Figure 11.4: input view interface

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.

The sequence of lines is terminated by a special symbol, say, !. A payroll application


reads the lines one by one, extracts the tokens from each line, converts them to names
and numbers, and calculates paychecks. The effort spent on extracting names and
numbers is best placed into a class that acts as the application’s input view.
For the payroll application, the input view’s responsibilites are to extract a name,
hours worked, and payrate from each input record (line). Table 4 shows the interface
for the payroll application’s input view.
We can write an interface for the payroll application’s output view as well, which
will print the paychecks that are calculated. (Call the output view class PayrollWriter.)
Based on these interfaces, we might write the controller for the payroll application
that appears in Figure 5. The controller need not worry about the exact format of
the records in the input payroll file, and it need not worry about how paychecks are
nicely printed—these responsibilities are handled by the view objects.
Figure 6 presents one possible coding of the input view, class PayrollReader.
The hard work is performed within getNextRecord, which verifies the input file is
nonempty before it reads the next record. The method also verifies that the record
is not the end-of-file marker, !, and that the record indeed has a string name, an
integer hours worked, and a double payrate. If all these conditions hold true, then
the information from the record is saved, and true is returned. The method skips
over badly formatted records and returns false when the end of the input is reached.
An exception handler recovers from exceptions that arise when intValue and
doubleValue fail. Because we are forced to write a handler for these RuntimeExceptions,
we use that handler to catch a throw new RuntimeException(line), which announces
an incorrect quantity of data on an input line. Since getNextRecord must also cope
with a possible IOException, we attach two exception handlers to the same try clause.
11.4. CASE STUDY: PAYROLL PROCESSING 697

Figure 11.5: controller for payroll application


/** Payroll prints a file of paychecks from an input payroll file. */
public class Payroll
{ public static void main(String[] args)
{ String in name
= JOptionPane.showInputDialog("Please type input payroll name:");
String out name
= JOptionPane.showInputDialog("Please type output payroll name:");
if ( in name != null && out name != null )
{ processPayroll(in name, out name); }
System.out.println("finished");
}

private static void processPayroll(String in, String out)


{ PayrollReader reader = new PayrollReader(in);
PayrollWriter writer = new PayrollWriter(out);
while ( reader.getNextRecord() )
{ double pay = reader.hoursOf() * reader.payrateOf();
writer.printCheck(reader.nameOf(), pay);
}
reader.close();
writer.close();
}
}
698

Figure 11.6: input-view class for payroll processing


import java.io.*;
import java.util.*;
/** PayrollReader reads records from a sequential file. The records have
* the format, NAME|HOURS|PAYRATE. The file is terminated by a ! */
public class PayrollReader
{ private BufferedReader infile; // the address of the input file
private String END OF FILE = "!";
// the name, hours, and payrate of the most recently read record:
private String name;
private int hours;
private double payrate;

/** 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());
}
}

public String nameOf() { return name; }

public int hoursOf() { return hours; }

public double payrateOf() { return payrate; }

public void close()


{ try { infile.close(); }
catch (IOException e)
{ System.out.println("PayrollReader warning: file close failed"); }
}
...
11.4. CASE STUDY: PAYROLL PROCESSING 699

Figure 11.6: input-view class for payroll processing (concl.)


/** getNextRecord attempts to read a new payroll record.
* @return whether another record was read and is ready to process */
public boolean getNextRecord()
{ boolean result = false;
name = null;
hours = -1;
payrate = -0.1;
try { if ( infile.ready() )
{ String line = infile.readLine();
StringTokenizer t = new StringTokenizer(line, "|");
String s = t.nextToken().trim();
if ( ! s.equals(END OF FILE) ) // finished?
{ if ( t.countTokens() == 2 ) // hours and payrate?
{ name = s;
hours = new Integer
(t.nextToken().trim()).intValue();
payrate = new Double
(t.nextToken().trim()).doubleValue();
result = true;
}
else { throw new RuntimeException(line); }
}
}
}
catch (IOException e)
{ System.out.println("PayrollReader error: " + e.getMessage()); }
catch (RuntimeException e)
{ System.out.println("PayrollReader error: bad record format: "
+ e.getMessage() + " Skipping record");
result = getNextRecord(); // try again
}
return result;
}
}
700

These techniques with exceptions are examined in the sections that follow.

Exercise
Specify and write class PayrollWriter for the payroll example.

11.5 Exceptions and Exception Handlers


Even though it might be well written, a program can receive bad input data, for
example, a sequence of letters when a number is required. When this has happened
with earlier examples, the program stopped in the middle of its execution and an-
nounced that an error (exception) occurred. How can a program protect itself from
this occurrence?
The Java language provides a construction, called an exception handler, that pro-
vides protection. Here is an example: A program must compute a division with an
integer that is supplied as input:
import javax.swing.*;
/** DivideIntoTwelve reads an int and divides it into 12 */
public class DivideIntoTwelve
{ public static void main(String[] args)
{ int i = readAnInt();
JOptionPane.showMessageDialog(null, "Answer is " + (12 / i));
System.out.println("Finished.");
}

/** readAnInt reads an int and returns it */


private static int readAnInt()
{ String s = JOptionPane.showInputDialog("Please type an int:");
int num = new Integer(s).intValue();
return num;
}
}

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:

Type an int: one


Exception in thread "main" java.lang.NumberFormatException: one
11.5. EXCEPTIONS AND EXCEPTION HANDLERS 701

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)

The message documents the methods incompleted due to the exception.


Both errors are examples of runtime exceptions, so named because they occur
when the program is “running.” The wordy messages produced from exceptions are
confusing to the user, and an application can do better about reporting the exceptions.
We improve the program by inserting exception handlers. An exception handler
is a kind of protective “cover” that surrounds a questionable statement and helps the
program recover from exceptions generated by the statement.
To help the program recover from division-by-zero errors, we surround the state-
ment with the division operation with an exception handler (note the keywords, try
and catch):

public static void main(String[] args)


{ int i = readAnInt();
try { JOptionPane.showMessageDialog(null, "Answer is " + (12 / i)); }
catch(RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"Error in input: " + i + ". Restart program.");
}
System.out.println("Finished.");
}

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.

11.5.1 Restarting a Method with an Exception Handler


The example in the previous section used a private method, readAnInt, to read an
integer from its user. What if the user types letters instead of an integer, e.g., four
instead of 4? To handle this situation, readAnInt can employ an exception handler
that gives the application’s user multiple tries at typing a proper integer—if the user
types an improper input, the exception handler’s catch-section restarts readAnInt by
sending a message to the method itself:
/** readAnInt reads an int and returns it */
private static int readAnInt()
{ int num;
String s = JOptionPane.showInputDialog("Please type an int:");
try { num = new Integer(s).intValue(); }
catch(RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"Input " + s + " not an int; try again!");
num = readAnInt(); // restart with a recursive invocation!
}
return num;
}

When a non-integer is supplied as input, a runtime exception is thrown at new


Integer(s), and the catch section goes to work: a dialog appears, then a message is
sent to the method to restart itself, giving the user a new chance to type an integer.
When a method restarts itself, it is called a recursive invocation.
11.5. EXCEPTIONS AND EXCEPTION HANDLERS 703

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"

try{ 2> num = new Integer(s).intValue(); }


catch( ... ) { ... }
return num;
}

The attempted conversion of four to an integer throws a runtime exception, and


execution moves into the catch section:

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

restarted method proceeds without exception:


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
{ 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

Figure 11.7: interface for input views

This is a standardized interface for


a class that reads interactive in-
put.
Methods (responsibilities)
readString(String prompt): prompt the user for input with prompt, read a
String string, and return it as the result
readInt(String prompt): int prompt the user for input with prompt, read an
integer, and return it as the result
readDouble(String prompt): prompt the user for input with prompt, read a dou-
double ble, and return it as the result

will use recursive invocations to restart methods; Chapters 7 and 12 present other
uses for recursive invocations.

11.5.2 Interactive Input with Exception Handlers


Whenever we required interactive input, we wrote a sequence of statements like the
following:

String input = JOptionPane.showInputDialog("Type an integer:");


int value = new Integer(input).intValue();

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:

DialogReader reader = new DialogReader();


int value = reader.readInt("Type an integer:");

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:

public static void main(String[] args)


{ DialogReader reader = new DialogReader(); // the new input-view
int c = reader.readInt("Type an integer Celsius temperature:");
706

// this remains unchanged:


double f = celsiusIntoFahrenheit(c);
DecimalFormat formatter = new DecimalFormat("0.0");
System.out.println("For Celsius degrees " + c + ",");
System.out.println("Degrees Fahrenheit = " + formatter.format(f));
}

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:

answer = new Integer(s.trim()).intValue();

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.

11.6 Exceptions Are Objects


We have seen how exceptions can terminate a program’s execution and how an excep-
tion handler can trap an exception so that execution can resume. Now, we learn that
exceptions are objects that bind to the formal parameters of of exception handlers.
When an error (such as division by zero) occurs, the program must collect infor-
mation about the error and deliver the information to a handler of the exception.
The information collected about the error usually comes in two parts:

• a description of the nature of the error (e.g., “division by zero”);

• 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

Figure 11.8: class for input via a dialog


import javax.swing.*;
/** DialogReader accepts user input from a dialog */
public class DialogReader
{ /** Constructor DialogReader initializes the input view, a dialog */
public DialogReader() { } // nothing to initialize

/** readString reads a string from the input source.


* @param prompt - the prompt that prompts the user for input
* @return the string the user types in response */
public String readString(String prompt)
{ return JOptionPane.showInputDialog(prompt); }

/** readInt reads an integer from the input source


* @param prompt - the prompt that prompts the user for input
* @return the integer supplied in response */
public int readInt(String prompt)
{ int answer = 0;
String s = readString(prompt);
try { answer = new Integer(s.trim()).intValue(); }
catch (RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"DialogReader error: " + s + " not an int.");
answer = readInt(prompt); // restart
}
return answer;
}

/** readDouble reads a double from the input source


* @param prompt - the prompt that prompts the user for input
* @return the double supplied in response */
public double readDouble(String prompt)
{ double answer = 0;
String s = readString(prompt);
try { answer = new Double(s.trim()).doubleValue(); }
catch (RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"DialogReader error: " + s + " not a double.");
answer = readDouble(prompt); // restart
}
return answer;
}
}
708

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 }

It defines a method, catch, that is “invoked” within STATEMENTS if an exception occurs


there. If an exception object is created within STATEMENTS, and if the exception’s type
is a subtype of EXCEPTION TYPE, then the exception object binds to formal parameter
e, and HANDLER executes. (If the exception’s type is not a subtype of EXCEPTION CLASS,
then the exception “escapes” to find its handler.) Once HANDLER finishes, execution
continues with the statements that follow it. Within HANDLER, messages can be sent
to the exception object, e.g., e.printStackTrace().
If a programmer writes an exception handler, she is obligated to make the han-
dler repair the error to a degree that execution can safely proceed. In this regard,
programmer-written exception handlers can prove dangerous, because it is all too
easy for a programmer to do too little to repair an error.
Here is a simple example. Perhaps we must write a method, readAnIntFrom, which
reads a string from the input-view object and converts it into a string. An exception
handler is added to cope with the possibility that the input string is not an integer:

/** readAnIntFrom reads an int from input-view, view, and returns it */


11.6. EXCEPTIONS ARE OBJECTS 709

Figure 11.9: classes of exceptions


Object
|
+-Throwable: getMessage():String, toString():String, printStackTrace()
|
+-Exception
|
+-InterruptedException
|
+-RuntimeException
| |
| +-ArithmeticException
| |
| +-ArrayIndexOutOfBoundsException
| |
| +-ClassCastException
| |
| +-NullPointerException
| |
| +-NumberFormatException
| |
| +-StringIndexOutOfBoundsException
| |
| +-NoSuchElementException
|
+-IOException
|
+-FileNotFoundException
|
+-EOFException
|
+-InterruptedIOException
710

Figure 11.10: methods for exception objects

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.

private int readAnIntFrom(BufferedReader view) throws IOException


{ int num;
System.out.print("Type an int: ");
String s = view.readLine();
try { num = new Integer(s).intValue(); }
catch (RuntimeException e)
{ System.out.println("Non-integer error");
num = -1;
}
return num;
}

¡/pre¿ If the string returned by readLine is nonnumeric, then a runtime exception


arises at new Integer(s).intValue(). A NumberFormatException object is created
and is promptly caught by the enclosing catch, which merely prints a message and
lets the method return -1 as its result. Since -1 might be confused for a legitimate
integer input, the exception handler has not done a good enough job. We improve it
as seen in Figure 11.
In the Figure, the handler insists that the user type another string, and it invokes
itself to restart the process. This ensures that the method concludes only when a
proper integer has been typed. The message, e.getMessage(), extracts information
about the nature of the error. Finally, the exception handler is refined to handle only
NumberFormatExceptions, which ensures that the handler is used only for the form of
error it is prepared to handle, namely, a format error for a string that is nonnumeric.
When an exception is thrown, it is not always obvious which catch-construct will
handle it. If an exception is thrown within a method, and if the method does not
catch the exception, then the exception seeks its handler by returning to the method
that invoked the erroneous method and searching there.
The following artificial example explores this idea. Perhaps we have

ExceptionExample ex = new ExceptionExample();


11.6. EXCEPTIONS ARE OBJECTS 711

Figure 11.11: improved exception handling for reading integers


/** readAnIntFrom reads an integer from the input source
* @param view - the input-view object
* @return the integer supplied in response. (And keep trying until the user
* supplies a legal integer!) */
private int readAnIntFrom(BufferedReader view) throws IOException
{ int num;
System.out.print("Type an int: ");
String s = view.readLine();
try { num = new Integer(s).intValue(); }
catch(NumberFormatException e)
{ JOptionPane.showMessageDialog(null, "Error: " + e.getMessage()
+ " not an integer; try again.");
num = readAnIntFrom(view); // restart
}
return num;
}

ex.f();

where class ExceptionExample is defined as

import java.io.*;
public class ExceptionExample
{ public ExceptionExample() { }

public void f()


{ try { g(); }
catch (RuntimeException e) { System.out.println("caught at f"); }
System.out.println("f completes");
}

public void g()


{ try { PrintWriter outfile = new PrintWriter(new FileWriter("text.out"));
try { outfile.println( h() ); }
catch (NullPointerException e)
{System.out.println("null pointer caught at g"); }
}
catch (IOException e)
{ System.out.println("io error caught at g"); }
System.out.println("g completes");
}
712

private int h()


{ int[] r = new int[2];
return r[3];
}
}

The message to f causes a message to g which causes a message to h, where an


array indexing error occurs. Within h, an ArrayIndexOutOfBoundsException ob-
ject is created, and the plan of returning an integer from h is abandoned. In-
stead, the exception object seeks its handler. No handler appears in h, so the ex-
ception returns to the position where h was invoked. This places it at the state-
ment, outfile.println( h() ) within g. The plan to perform the outfile.println
is abandoned; the exception seeks its handler instead. It examines the data type,
NullPointerException, of the nearest enclosing handler, but the type does is not a
subtype of ArrayIndexOutOfBoundsException, so the exception object searches fur-
ther. The next enclosing handler has type IOException, but this is also unacceptable,
so the exception returns from g to the position, g(), within method f.
Here, the exception finds a handler that can accommodate it: The exception’s
type is a subtype of RuntimeException, so the message, caught at f, is printed. The
handler consumes the exception, and normal execution resumes at the statement,
System.out.println("f completes"), which prints. Execution concludes at ex.f(),
which is unaware of the problems that occurred.
The above example was meant to illustrate a technical point; it is not an example of
good programming style. Indeed, exception handlers are best used to help a program
recover from actions over which it has no influence, e.g., a user typing invalid input.
Exception handlers are not so useful for detecting internal programming errors such
as array indexing errors. As a rule,

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.

2. Revise class CopyFile in Figure 3 so that an exception handler catches the


FileNotFoundException that might arise when a user supplies an invalid name
for the input file. The exception handler tells the user of the error and asks her
to type a new file name.

11.6.1 Programmer-Generated Exceptions


In the rare case, it is possible for a programmer to escape from a disastrous situation
by constructing an exception object:
try { ... // perhaps some complicated computation is undertaken
if ( some_bad_condition_is_true )
{ throw new RuntimeException("terrible error"); }
... // otherwise, continue with the complicated computation
}
catch (RuntimeException e)
{ ... e.getMessage() ... } // try to handle the bad condition

The statement, throw new RuntimeException(S), constructs a runtime exception ob-


ject and “throws” to its handler. String S is the message embedded within the ex-
ception and is the answer produced by a getMessage.
If one wants to be certain that the explicitly generated exception is not inadver-
tantly confused with the other forms of runtime exceptions, one can create a new
class for the new exception, e.g.,
714

public class ComplicatedException extends Exception


{ private int villain; // the integer that caused all the trouble

public ComplicatedException(String error_message, int the_bad_number)


{ super(error_message);
villain = the_bad_number;
}

public int getVillain()


{ return villain; }
}

Here, class ComplicatedException is a subclass of Exception, meaning that its ob-


jects can be “thrown.” Its constructor method has been enhanced to accept both an
error message and an integer, which might be useful to its exception handler. The
new class of exception might be used as follows:

try { int x = ... ;


... // some complicated computation is undertaken
if ( some_bad_condition_is_true_about(x) )
{ throw new ComplicatedException("terrible error", x); }
... // otherwise, continue with the complicated computation
}
catch (ComplicatedException e)
{ ... e.getMessage() ...
int i = e.getVillain();
...
}

The statement, throw new ComplicatedException("terrible error", x), builds a


ComplicatedException object and throws it to its handler, which sends messages
to the exception to learn the nature of the error.

11.7 Summary
We summarize the main points of the chapter.

New Construction
• exception handler:

public int readInt(String prompt)


{ int answer = 0;
String s = readString(prompt);
11.7. SUMMARY 715

try { answer = new Integer(s.trim()).intValue(); }


catch (RuntimeException e)
{ JOptionPane.showMessageDialog(null,
"DialogReader error: " + s + " not an int.");
answer = readInt(prompt); // restart
}
return answer;
}

New Terminology
• token: a “word” within a string or a line of text

• delimiter: a character that is used to separate tokens in a string; examples are


spaces and punctuation.

• file: a collection of symbols stored together on a disk

• 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

1. a character file, which is a sequence of keyboard symbols, saved in the file


as a sequence of bytes;
2. a binary file, which is a sequence of ones-and-zeros.

• 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:

• In Java, an output file of characters is most simply created from a PrintWriter


object, e.g.,

PrintWriter outfile = new PrintWriter(new FileWriter("test.txt"));


716

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.,

BufferedReader infile = new BufferedReader(new FileReader("test.txt"));

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.

New Classes for Future Use


• class StringTokenizer: used to disassemble a string into its constituent tokens;
see Table 1.
• class FileWriter: used to construct a connection to an output sequential file;
see Figure 2.
• class PrintWriter: used to write strings to an output sequential file; see Figure
2.
• class FileReader: used to construct a connection to an input sequential file;
see Figure 3.
• class BufferedReader: used to read lines from an input output sequential file;
see Figure 3.
• System.in: a preexisting object that can read symbols typed into the command
window.
• class InputStreamReader: used to connect to System.in.

11.8 Programming Projects


1. Write a text formatting program.
(a) The first version, PrettyPrint, reads a nonempty sequential file, and pro-
duces a sequential file whose lines are “pretty printed,” that is, words are
separated by exactly one blank and every input line has a length that is
as close as possible, but does not exceed, 80 characters. (To simplify this
Project, assume that no single word has a length that exceeds 80 charac-
ters.)
11.8. PROGRAMMING PROJECTS 717

(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

Mean = ( n_0 + n_1 + ... n_m ) / m


_______________________________________________
StdDev = \/ -(Mean^2) + (n_0)^2 + (n_1)^2 + ... + (n_m)^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).)

5. Write an application that counts occurrences of words in a sequential file:

(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.

7. Write a program that does simplistic text compression:

(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.

(Assume that \ does not appear in the input file.)


(b) Modify the program so that repetitions of length 10 or greater are com-
pressed as well.
(c) Write the corresponding uncompression program.

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:

• a numeral, n, translates to two integers, 0 and the integer n.


• a left parenthesis, (, translates to the integer, 1, and a right parenthesis,
), translates to the integer, 2.
• the operators, +, -, *, ==, < , <=, &&, and ! translate to the integers 3
through 10, respectively.

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

Figure 11.12: copying a file character by character


import java.io.*;
/** CharCopy copies in.dat to out.dat character by character */
public class CharCopy
{ public static void main(String[] args) throws IOException
{ FileReader infile = new FileReader("in.dat");
FileWriter outfile = new FileWriter("out.dat");
while ( infile.ready() )
{ int c = infile.read(); // reads (the coding of) a character
outfile.write(c); // writes the character
}
infile.close();
outfile.close();
}
}

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


11.9.1 Character-by-Character File Processing

11.9.2 Binary Files and Files of Objects

11.9.3 A Taxonomy of File Classes

11.9.4 A GUI for File Selection

Here are some optional topics related to file processing.

11.9.1 Character-by-Character File Processing


It is tedious but nonetheless possible to process sequential files one character at a
time. Figure 12 shows an application that copies the contents of a input file to an
output file this way.
For reasons explained at the end of the chapter, it suffices to work with a FileReader
object to read character input and a FileWriter object to write character output.
720

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.

11.9.2 Binary Files and Files of Objects


When you state,
double d = 1000007.5;
System.out.println(d);
A binary representation of one-million-seven-and-a-half is saved in a single cell, and
this representation is fetched and coverted into a sequence of nine characters for
display on the console window. Conversions of numbers to characters are time con-
suming, and it is best to avoid them.
If a huge array of numbers must be saved in a file for later computation, it
is best to use a binary file. Java provides class ObjectOutputStream and class
ObjectInputStream for writing and reading binary files of numbers, booleans, and ar-
bitrary objects. The values are copied in their binary, storage representations; there
is no conversion to character format.
For example, to write the above number to a binary file, data.out, we create an
output-file object and send a writeDouble message to it:
double d = 1000007.5;
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream("data.out"));
out.writeDouble(d);
Similarly, to read a complete file, data.in, of doubles, we would state
ObjectInputStream in = new ObjectInputStream
(new FileInputStream("data.in"));
while ( in.available() > 0 ) // more data left to read?
{ double d = in.readDouble();
...
}
in.close();
11.9. BEYOND THE BASICS 721

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.

11.9.3 A Taxonomy of File Classes


The java.io package holds a variety of classes for input and output; Figure 13 lists
those classes used in this text. Because of a history of alterations to the package,
the intentions behind the class hierarchy are less clear than they might be. When
studying the hierarchy, keep these ideas in mind:
• In Java, data are organized as sequences or streams of bytes. An input stream
can be read; an output stream is written.
• 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.
722

Figure 11.13: writing and reading objects from a file


import java.io.*;
/** ObjectFileTest writes and reads two objects and an integer */
public class ObjectFileTest
{ public static void main(String[] args)
throws IOException, ClassNotFoundException
{ Cell b = new Cell(5);
ComposedCell c = new ComposedCell(b);
Cell d = new Cell(7);
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream("test.ob"));
out.writeObject(c);
out.writeInt(49);
out.writeObject(d);
out.close();
// now, read the objects and the integer and restore them:
ObjectInputStream in = new ObjectInputStream
(new FileInputStream("test.ob"));
Object ob = in.readObject();
ComposedCell m = (ComposedCell)ob; // cast ob into a ComposedCell
System.out.println(m.answerOf());
System.out.println(in.readInt());
ob = in.readObject();
Cell n = (Cell)ob; // cast ob into a Cell
System.out.println(n.valueOf());
in.close();
}
}

/** ComposedCell builds an object that holds within it two objects */


public class ComposedCell implements Serializable
{ private Cell x;
private Cell y;

public ComposedCell(Cell my cell)


{ x = my cell;
y = new Cell(0);
}

public int answerOf()


{ return x.valueOf() + y.valueOf(); }
}
11.9. BEYOND THE BASICS 723

Figure 11.13: writing and reading objects from a file (concl.)


/** Cell holds an integer */
public class Cell implements Serializable
{ private int value;

public Cell(int start) { value = start; }


public int valueOf() { return value; }
}

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

Figure 11.14: class hierarchy for input-output


Object
|
+-File: File(String), getPath():String, isFile():boolean, isDirectory():Boolean
|
+-InputStream (abstract): available():int, close()
| |
| +-FileInputStream: FileInputStream(String), FileInputStream(File)
| | read():int
| |
| +-FilterInputStream: read():int
| |
| +-ObjectInputStream: ObjectInputStream(InputStream), read():int,
| readInt():int, readDouble():double, readBoolean():boolean,
| readObject():Object
|
+-OutputStream (abstract): close()
| |
| +-FileOutputStream: FileOutputStream(String), FileOutputStream(File),
| | write(int)
| |
| +-FilterOutputStream: write(int)
| |
| +-ObjectOutputStream: ObjectOutputStream(OutputStream), write(int),
| writeInt(int), writeDouble(double), writeBoolean(boolean),
| writeObject(Object)
|
+-Reader (abstract)
| |
| +-InputStreamReader: InputStreamReader(InputStream), read():int,
| | | ready():boolean, close()
| | |
| | +-FileReader: FileReader(String)
| |
| +-BufferedReader: BufferedReader(Reader), read():int, ready():boolean,
| readLine():String, close()
|
+-Writer (abstract)
|
+-OutputStreamWriter: OutputStreamWriter(OutputStream), write(int), close()
| |
| +-FileWriter: FileWriter(String)
|
+-PrintWriter: PrintWriter(Writer), PrintWriter(OutputStream), write(int),
print(int), print(double), print(boolean), print(Object),
println(int), println(double), println(boolean), println(Object),
println(), close()
11.9. BEYOND THE BASICS 725

class File contains the path name and basic properties of


a file
Constructor
File(String path) Constructs the file object from the complete
path name, path.
Methods
getPath(): String Return the complete directory path name of the
file object.
isFile(): boolean Return whether the file object is a file.
isDirectory(): boolean Return whether the file object is a directory.

abstract class InputStream a sequence (“stream”) of data items that can


be read one byte at a time
Methods
available(): int Return how many bytes are available for read-
ing.
close() Close the file.

class FileInputStream a stream that is a sequential disk file


extends InputStream
Constructors
FileInputStream(String path) Open the disk file whose path name is path.
FileInputStream(File f) Open the disk file named by f.
Method
read(): int Read the next unread byte from the file and
return it; return -1 if the file is at the end.

class FilterInputStream a basic input stream, e.g., from the keyboard.


extends InputStream
Method
read(): int Return the next byte from the stream; return
-1 if the end of the stream is reached.
726

class ObjectInputStream an input stream from which binary values


extends InputStream (primitive values and objects) can be read
Constructor
ObjectInputStream(InputStream Uses stream to construct an object-input
stream) stream
Methods
read(): int Return the next byte from the stream; return
-1 if the end of the stream is reached.
readInt(): int Return a 32-bit integer assembled from bytes
read from the stream; throws EOFException if
end of the stream is reached.
readDouble(): double, Similar to readInt().
readBoolean(): boolean
readObject(): Object Return an object assembled from bytes read
from the stream; throws an exception if a com-
plete object cannot be assembled.

abstract class OutputStream a sequence (“stream”) of data items that have


been written
Method
close() Close the file.

class FileOutputStream an output stream that is a sequential disk file


Constructors
FileOutputStream(String Open the disk file whose path name is path.
path)
FileOutputStream(File f) Open the disk file named by f.
Methods
write(int b) Write byte b to the file.
close() Close the file.

class FilterOutputStream a basic output stream, e.g., to the console win-


extends OutputStream dow.
Method
write(int b) Write byte b to the output stream.
11.9. BEYOND THE BASICS 727

class ObjectOutputStream an output stream to which binary values (prim-


extends OutputStream itive values and objects) can be written
Constructor
ObjectOutputStream(OutputStreamUses stream to construct an object-output
stream) stream
Methods
write(int b) Write byte b to the stream.
writeInt(int i) Writes i, a 32-bit integer, to the stream.
writeDouble(double d), Similar to writeInt().
writeBoolean(boolean b)
writeObject(Object ob) Writes ob to the output stream, provided
that the class from which ob was constructed
implements Serializable; throws an excep-
tion, otherwise.

abstract class Reader a sequence of characters that can be read

class InputStreamReader a basic character stream


extends Reader
Constructor
InputStreamReader(InputStream Construct a character stream that reads bytes
stream) from stream to assemble its characters.
Methods
read(): int Read the next unread character from the stream
and return it as an integer; return -1 if the end
of the stream is reached.
ready(): boolean Return whether the stream contains more char-
acters to be read.
close() Close the stream.

class FileReader extends an InputStreamReader constructed from a


Reader FileInputStream.
Constructors
FileReader(String s), Constructs the input-stream reader—an ab-
FileReader(File s), breviation for new InputStreamReader(new
FileInputStream(s)).
728

class BufferedReader extends a character stream from which complete lines of


Reader text can be read
Constructor
BufferedReader(Reader r) Construct a buffered reader that reads its char-
acters from r.
Methods
read(): int Read the next unread character from the stream
and return it as an integer; return -1 if the end
of the stream is reached.
readLine(): String Read a line of text from the stream, that is, a
maximal sequence of characters terminated by
a newline (’\n’) or return-newline (’\r’ and
’\n’). Return the line less its terminator char-
acters; return null if the end of the stream is
reached.
ready(): boolean Return whether the stream contains more char-
acters to be read.
close() Close the stream.

abstract class Writer a writable sequence of characters

class OutputStreamWriter a basic character stream


extends Writer
Constructor
OutputStreamWriter(OutputStreamConstruct a character stream to which charac-
stream) ters can be written by means of stream.
Methods
write(int c) Write character c to the stream.
close() Close the stream.

class FileWriter extends an OutputStreamWriter constructed from a


Writer FileOutputStream
Constructors
FileWriter(String s), Constructs the output-stream writer—an
FileWriter(File s), abbreviation for new OutputStreamWriter(new
FileOutputStream(s)).
11.9. BEYOND THE BASICS 729

class PrintWriter extends a buffered character stream to which primi-


Writer tive values and objects can be written (in their
string depictions)
Constructors
PrintWriter(OutputStream s), Construct a print writer that sends its values as
PrintWriter(Writer s) character sequences to s.
Methods
write(int c) Write character c to the output stream.
print(int d), print(double Write the character representation of d to the
d), print(boolean d), output stream. (When d is an object, d’s
print(Object d) toString method is used to get its representa-
tion.)
println(int d), Like print but appends line termination char-
println(double d), acter(s).
println(boolean d),
println(Object d) println()
close() Close the stream.
730

11.9.4 A GUI for File Selection


The javax.swing package contains a component from which you can create a dialog
that displays a folder (directory); the object, called a file chooser, looks like this:

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

Figure 11.15: methods for JFileChooser

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

showDialog(Component Display the file chooser as a dialog, using owner as


owner, String the dialog’s owner component. The approval but-
text for approve button) ton is labelled with text for approve button.

{ 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

Java Language Definition


This section defines the Java subset used in this text. (Consult The Java Language
Specification, by J. Gosling, B. Joy, and G. Steele, Addison Wesley Publishing, 1996,
for the definition of the full Java language.)
A language is defined by its syntax (its spelling and grammar) and its semantics
(what its sentences mean). We will define syntax with the grammatical notation
explained in the next section, and we will give semantics by means of English ex-
planation and examples. Also, references will appear to the relevant sections of the
text.

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:

NUMBER ::= WHOLE_PART FRACTION_PART?


WHOLE_PART ::= NUMERAL_SEQUENCE
FRACTION_PART ::= . NUMERAL_SEQUENCE
NUMERAL_SEQUENCE ::= NUMERAL NUMERAL*
NUMERAL ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9

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:

NUMBER ::= NUMERAL_SEQUENCE [[ . NUMERAL_SEQUENCE ]]?


725

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:

public abstract class MyFrame extends Frame implements MyInterface


{ private int i = 2;
...
public MyFrame() { ... }
...
public void f() { i = i + 1; }
public void paint(Graphics g) { ... }
public abstract int h(GregorianCalendar g);
...
}
727

Semantics: A class defines a “blueprint” from which objects are constructed.


Chapter 2 introduces classes; see also “Revised Syntax and Semantics of Classes” at
the end of Chapter 5.
An abstract class lacks codings for some of its methods, and objects cannot be
constructed directly from an abstract class (see “Abstract Classes” in Chapter 9)—
another class must be written that extends the abstract class. A final class cannot
be extended (see “final Components” in Chapter 9). A class can extend at most one
other class but it can implement multiple interfaces.

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)

DECLARATION is defined in a later section.


Examples:
private int i = 2;
public static final double ONE = 1.0;
private static boolean b;
private GregorianCalendar today = new GregorianCalendar();
Semantics: A field is a variable declaration that exists independently of the meth-
ods that use it. (See “Objects with State: Field Variables” and “Scope of Field
Declarations” in Chapter 4.) A final field cannot not be altered in value once it is
initialized. A static field can be used by static methods, like main. As illustrated in
“Case Study: Card Games,” in Chapter 8, a public static final field defines a constant
name that can be used throughout a program.
The forms of visibility are
728

• 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

• protected: the component can be referenced by components in subclasses of


the class in which this component appears and by components in classes in the
same package as the one in which this component appears

• (nothing): the component can be referenced by components in classes in the


same package as the one in which this component appears

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);
}

public MyFrame(int width, String title)


729

{ setTitle(title);
setSize(width, width/2);
setVisible(true);
}

public MyCustomButton(String label)


{ super(label);
setBackground(Color.red);
}

Semantics: When an object is constructed from a class, the object is constructed


in computer storage and the class’s constructor method is executed. Chapter 5 intro-
duces and uses extensively constructor methods; see “Revised Syntax and Semantics
of Classes” at the end of that chapter for technical details. The semantics of executing
a constructor method is the same as the semantics of executing a method, and the
section on methods that follows should be studied.

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

Semantics: As explained in “Data Type Checking” in Chapter 3, types in Java


are classified as primitive or reference types. Elements of the former are atomic,
non-object values; the latter are objects. Every class and interface defines a data
type; objects constructed from a class are the elements of that class’s type. If a class
implements an interface, the class’s objects also belong to the interface’s type.
Types are related by a subtyping relation; this is explained in Appendix II. A
RETURN TYPE is the type of value that a method may return as its result; void means
“no result returned.”

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; }

public abstract int h(GregorianCalendar g);

public static void main(String[] args)


{ new MyFrame(300, "My Test Frame"); }

Semantics: A method is invoked by a client, which supplies arguments to the


formal parameters in the method’s header. The arguments bind to the formal pa-
rameter names, and the statements in the method’s body execute. See Chapter 5
for extensive examples and see “Formal Description of Methods” in Chapter 5 for a
precise description of invocation, binding, and execution.
A class can have multiple methods with the same name, provided that each method
is uniquely identified by the quantity and data types of its formal parameters. (Such
a situation is called overloading the method name; see “Method Overloading” and
“Semantics of Overloading” in Chapter 9 for details.)
If a class extends a superclass, then the class can contain a method whose name
and formal parameters are identical to those of a method in the superclass. This is
called overriding the method. See “Subclasses and Method Overriding” and “Seman-
tics of Overriding” in Chapter 9 for extensive explanation.

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

GregorianCalendar g = new GregorianCalendar(); // object construction


System.out.println(Math.abs(-2) + 3); // invocation
int i; int j = (i = 3) + 1; // assignment: set i to 3 and then j to 4

The last usage of a statement expression is not encouraged.

Declaration
DECLARATION ::= TYPE IDENTIFIER [[ = INITIAL_EXPRESSION ]]? ;

INITIAL EXPRESSION is defined in “Expression”; its data type must be a subtype of


the TYPE in the declaration.
Examples:

int i = 2 + 3;
GregorianCalendar[] days_in_this_month;

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 sections, “Declarations and Variables” “Local Variables and
Scope,” in Chapter 3 provide examples and details.

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:

return 2 * sum(3, 4);


return new int[3];
return i;

Semantics: The return-statement calculates a value, which is immediately re-


turned as the result of a method. See “Results from Methods” in Chapter 5.

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:

public class Test


{ private static final int k = 2;

public static void main(String[] args)


{ int x = 0;
switch (x + 1)
{ case ((int)(k / 3.3) + 1) : { break; }
default : { }
}
}
}

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; }
}

Semantics: The appropriate arm of an if- or switch-statement is selected based


upon the value of the test expression. Chapter 6 presents numerous examples of if-
statements; see “The Switch Statement” at the end of that chapter for examples of
the switch-statement.

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; }

public static void main(String[] args)


{ System.out.println( sum(2, 3.4) ); }
}
Public methods can be invoked this way, also:
import javax.swing.*;
public class MyFrame extends JFrame
{ public MyFrame()
{ setTitle("MyFrame");
f("MyFrame");
this.f("MyFrame");
}

public void f(String label)


{ this.setTitle(label); }

...
}
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:

GregorianCalendar g = new GregorianCalendar();


... g.getDate(); ...

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.,

GregorianCalendar[] dates = new GregorianCalendar[3];


...
System.out.println( dates[0].getTime() );

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

A variable can be the element of an array, as is r[2] in int[] r = new int[3];


r[2] = 0. In this case, the address of the array object, r is first computed as a
RECEIVER, and the address is used with the index. (See the explanation of RECEIVER
in the previous section.)
If an object possesses public fields, the variable part of an assignment can be a
reference to one of those fields:

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 LITERAL, e.g., 3, false, or "abc", computes to itself. (Exception: a STRING LITERAL


computes to the address of an object that holds a sequence of characters, but
one can pretend the object and the character sequence are the “same.”)

• 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.

• An expression can be parenthesized to indicate the order in which infix and


prefix operations should be performed in compound expressions

• (TYPE)EXPRESSSION, a cast, computes its expression’s value and attempts to per-


form the cast of the value into the indicated data type. (If the value is a primi-
tive type, the cast may well change the internal representation of the value, e.g.,
(int)(5/2) forces 2.5 to 2. A cast on a value of reference type—an address—
does not change the value itself but only the data type of the value, as it is
understood by the Java compiler, e.g, MyFrame f = new MyFrame(); (JFrame)f
leaves the underlying object, f, unaltered but treats it as a mere JFrame.
• EXPRESSION instanceof REFERENCE TYPE computes the value of its expression
part, which will be (the address of) an object. The data type embedded in the
object is checked to see if it is a subtype of the REFERENCE TYPE. For example,
the conditional’s test expression in

Frame f = new MyFrame();


if ( f instance MyFrame ) { ... }

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.

Literal and Identifier


The forms of literals are listed in Table 2, Chapter 3. Identifiers are defined by the
section, “What is an Identifier?,” at the end of Chapter 3.
741

Appendix II

Typing and Subtyping


There are two forms of data types in the Java language:

• primitive types, such as int, double, and boolean

• reference (object) types, such as GregorianCalendar, int[], and String

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.

• Whatever their computed values, i + 1 and i / 2 will be integers that are


acceptable arguments to setSize.

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;

Although i is int-typed, it is allowed in the context, double d = * 2.5, because


int <= double, that is, int is a subtype of double.
Another context is the binding of actual parameters to formal parameters: A
method that expects an argument of type T1 will execute correctly if it receives an
argument of type T2 such that T2 <= T1 holds true. An example is
private void f(double x) { ... }
...
f(3);

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.

Subtypes of Primitive Types


The primitive types used in this text are boolean, char, byte, int, long, float, and
double; see Chapter 3. The numeric types in the above list are related by subtyping
as follows:
byte <= int <= long <= float <= double

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

Subtypes of Reference Types


Reference types are related by subtyping according to the following rules. Given
arbitrary reference types, T1 and T2, we say that T1 <= T2 when one of the following
rules holds true:

• T1 is the same name as T2

• T1 is defined by class T1 extends T2 or by class T1 implements T2 or by


interface T1 extends T2

• 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 { ... }

public interface I2 extends I1 { ... }

public class C implements I1 { ... }

public class D extends C implements I2 { ... }

we can conclude, say, D[] <= I1[], because

• class D extends C, hence, D <= C

• class C implements I1, hence, C <= I1

• D <= I1, because of the previous two clauses

• D[] <= I1[], because of the previous clause

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

Class Diagrams and Hierarchies


Programs are designed and documented with class diagrams. A class diagram
consists of a collection of components (names of classes and interfaces) connected by
lines and arrows that represent collaborations and dependencies. The class diagrams
used in this text are drawn in a subset of the diagram language of UML (Universal
Modelling Language; see UML Distilled, by M. Fowler, Addison-Wesley, 1999, for a
concise introduction.) Here is a summary of the diagram language.

Classes
In its simplest representation, a class is drawn as a rectangle containing the class’s
name.
BankAccount

A class can be presented with some or all of its public methods:


BankAccount
deposit
withdraw(int amount): boolean
getBalance(): int

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[]

¡/pre¿ See Figure 18, Chapter 9, for this example.


An interface is drawn like an abstract class—the name and all methods appear in
italics:
BankAccountSpecification
deposit(int amount)
withdraw(int amount): boolean

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 sends messages to the second

• the first component owns or holds the address (a “handle”) to the second in a
private field variable

• the first component cannot compile without the second

Here is an example from Chapter 7:

BounceController AnimationWriter
runAnimation() paintComponent(Graphics g)

BallWriter BoxWriter
paint(Graphics g) paint(Graphics g)

MovingBall Box
(see Table 6) (see Table 5)

A stronger form of dependence is called composition, which defines the “has-a”


746

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

Here, the coding of MortgageCalculator depends on interface BankAccountSpecification;


see Chapter 9 for this example.
A dotted arrow with an arrow head is drawn from a class to an interface when
the former implements the latter:
MortgageCalculator BankAccount
makeMortagagePayment() deposit(int amount)
withdraw(int amount): boolean
balanceOf(): int
BankAccountSpecification
deposit(int amount)
withdraw(int amount): boolean

Here, class BankAccount implements BankAccountSpecificaiton.


747

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

actionPerformed (of ActionListener): 10.3, 10.5


actual parameter (see also, parameter): 5.3, 5.3.1
add (of Container): 10.3, 10.4, 10.6, 10.7, 10.15.6
add (of JMenu): 10.3, 10.11, 10.11.1
add (of JMenuBar): 10.3, 10.11, 10.11.1, 10.15.6
addActionListener (of AbstractButton and JTextField): 10.3, 10.5, 10.9, 10.11.1,
10.15.6
addListSelectionListener (of JList): 10.3,10.8, 10.15.6
addMouseListener (of Component): 10.15.13
addObserver (of Observable): 10.3, 10.12, 10.15.6
addSeparator (of JMenu): 10.3
addTableModelListener (of TableModel): 10.15.2
addWindowListener (of Window): 10.3, 10.15.6
“alarm” for loop termination: 7.25.3
algorithm: 1.7, 2.1, 3.2.1, 5.6, 6.4, 7.3, 7.5.1, 7.6, 7.22, 8.2, 8.14.1, 8.14.2, 8.14.4,
8.14.4
animation: 7.9, 10.6.1, 10.15.4
applet: 4.8.4, 7.25.6, 10.15.1
applet (HTML command): 4.8.4, 10.15.1
appletviewer: 4.8.4
application programming interface (API): 2.9.3, 5.4, 5.9.2, 6.12.2, 9.8.1
application: 2.1
architecture diagram: see class diagram
architecture, model-view-controller: 1.5
architecture, software: 1.5
args (parameter of main): 3.8
argument part of a message (see also, parameter): 2.3, 3.1, 5.3
argument, program: 3.8
ArithmeticException: 3.9, 11.6
array, one-dimensional: 8.4
array, two-dimensional: 8.8
ArrayIndexOutOfBoundsException: 8.2, 11.6
ASCII: 11.2
AsciiArtWriter output view: 5.2.1
args[0], args[1]: see String[] args
assignment: 3.2.1, 3.13.3
available (of InputStream): 11.9.3
AWTEvent: 10.3
\noindent
BallWriter view: 7.9
BankAccount model: 6.8.3, 6.8.4, 9.2
BankAccountSpecification interface: 9.2
758

BankWriter output view: 6.8.5, 6.8.7


BarGraphWriter output view: 5.8
BasicAccount model: 9.12.1
BasicPerson model: 9.2.1
BasicRoom model: 9.9
basis step, of induction proof: 7.25.2
best matching method: 5.9.5, 9.12.5
binary arithmetic: 1.1
binary file: 11.2, 11.9.2
binary search: 8.14.2, 8.14.4, 8.14.4
binding of actual to formal parameter: 5.3, 5.3.1
bit: 1.1
body of a method: 5.1
body of a program: 2.3
BOLD (of Font): 10.3, 10.11, 10.15.6
boolean: 3.4, 6.3
Boolean: 9.7
border layout: 10.3, 10.6
BorderLayout: 10.3, 10.6
BounceController controller: 7.9
Box model: 7.9
Box output view: 7.9
break: 6.6, 7.5.2
brighter (of Color): 4.8.3
buffered stream: 11.9.3
BufferedReader: 11.2.2, 11.3, 11.9.3
BullsEyeWriter output view: 7.3.1
button (graphical): 10.3, 10.4
byte: 3.13.1
\noindent
Calendar: 4.8.2
Calendar.DAY OF MONTH: 4.8.2
Calendar.HOUR: 4.3.1, 4.8.2
Calendar.HOUR OF DAY: 4.8.2
Calendar.MINUTE: 4.3.1, 4.8.2
Calendar.MONTH: 4.8.2
Calendar.SECOND: 4.8.2
Calendar.YEAR: 4.8.2
Cancel button of input dialog: 4.1, 6.4
CANCEL OPTION (of JOptionPane): 10.10, 10.15.6
Card model: 8.7
CardDeck model: 8.7
759

CardPlayer model: 9.5


CardPlayerBehavior interface: 9.5.1
cast: 3.3, 3.13.1, 3.13.3, 4.8.1, 8.3, 9.4
CastVoteFrame view: 10.15.2
ceil (of Math): 3.13.2
cell, variable: 3.2.1
CelsiusToFahrenheit application: 3.3
CelsiusToFahrenheit application with program arguments: 3.8.1
CelsiusToFahrenheit2 application with interactive input: 4.1
CelsiusToFahrenheitFrame application/output view: 4.4.1
CENTER (of BorderLayout): 10.3, 10.6
CENTER (of FlowLayout): 10.3
char: 3.5
character: 3.5, 6.8.7, 6.6, 7.5.1
character stream: 11.9.3
charAt (of String): 3.5, 6.8.7, 7.5.1, 11.1
CheckingAccount model: 9.12.1
class: 1.5.1, 1.7.3, 5.9.6
class: 2.1, 2.9.1, 2.9.2, 5.9.6
class, abstract: see abstract class
class hierarchy: 9.5.1
class, syntax of: 2.9.1, 5.9.6
class diagram: 1.5.1, 2.1, 4.1, 6.8.2, 6.8.6, 7.9, 8.10, 9.2, 9.2.1, 9.9.1, 9.5, 10.5, 10.9,
10.11.1, 10.12.1
ClassCastException: 9.2.1, 11.6
ClassNotFoundException: 11.9.2
CLASSPATH: 9.8
clearSelection (of JList): 10.3,10.8, 10.15.6
client (message sender): 5.1
ClockWriter output view: 4.3.1
close a file: 11.2
close (of BufferedReader and InputStream): 11.2.2, 11.9.3
close (of InputStreamReader): 11.9.3
close (of OutputStream): 11.9.3
close (of OutputStreamWriter): 11.9.3
close (of PrintWriter): 11.2.1, 11.9.3
CLOSED OPTION (of JOptionPane): 10.10, 10.15.6
collaborator: 1.7.2, 6.8.2, 7.7, 7.7.1, 7.9
colors for graphics pen: 4.2.2, 4.8.3
Color: 4.2.2, 4.8.3
ColorButton controller: 10.6.1
column of an array: 8.8
760

comment: 2.3, 2.4


compareTo (of String): 3.5
compile a program: 2.2
compiler: 1.2
complexity of an algorithm: see time complexity
Component: 10.3, 10.15.6
component: 1.7.2, 7.7
component (graphical): 10.3
composition of classes: 10.1
computer: 1.1
ComputerPlayer model: 9.5
concrete class: 9.5
conditional statement: 6.2, 6.2.1
constructor method: 4.3.1, 5.2.2
container (graphical): 10.3
Container: 10.3, 10.15.6
content pane: 10.4
control flow: 6.1, 6.2, 6.2.1
control structure: 6.1
controller object: 1.5, 4.1, 10.1
ConvertHours application: 6.2
copy (of JTextComponent): 10.3, 10.11, 10.15.6
cos (of Math): 3.13.2, 5.9.3
counter: see loop counter
CountButton controller: 10.5
Counter model: 10.4
Counter3 model: 10.12
countTokens (of StringTokenizer): 11.1.1
coupling: 6.8.2, 6.8.6, 7.7.1, 10.4, 10.12.1
CRC cards: 7.7.1
customization of a class: 1.7.4 (See also: inheritance)
cut (of JTextComponent): 10.3, 10.11, 10.15.6
\noindent
darker (of Color): 4.8.3
data structure: Chapter 8
data type: 3.6, 3.12.3, 4.8.1, 5.9.4, 9.4
data type, primitive type: 3.12.3, 9.4
data type, reference type: 3.12.3, 9.4, 9.6
data type, run-time: 4.8.1, 8.4, 8.14.5, 9.4
data type, subtype: see: subtype
data type, user defined via class: 9.4
data-type checking: 3.12.3, 5.9.4, 5.9.5, 8.14.5, 9.4
761

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

exception: 3.9, 6.5.1, 10.9, 11.5, 11.5.1, 11.5.2, 11.6, 11.6.1


exception handler: 10.9, 11.5, 11.5.1, 11.6, 11.6.1
exit (of System): 6.5.3, 10.5
exponent: 3.3
extends: 4.2.2, 9.3
event: 10.2, 10.5
EventObject: 10.3
event-driven programming: 10.2, 10.5, 10.12
event handler: 10.2, 10.5
event handling: see event handler
event listener: 10.2, 10.12 (see also: event handler)
environment variable: 9.8
execution trace: 2.3.1, 6.9.4
ExitButton controller: 10.5
Explorer model: 9.9
\noindent
factorial function: 7.3, 7.11, 7.11.1
Fibonacci function: 7.11.2
field (variable): 4.4, 4.4.1, 4.4.2
field variable, scope of: 4.4.2, 5.9.5
FieldExample output view: 4.4
file: 1.1, 11.2
file, binary: 11.2
file, character: 11.2
file, random access: 11.2
file, sequential: 11.2
File: 11.9.3
FileInputStream: 11.3, 11.9.2, 11.9.3
FileNotFoundException: 11.2.2, 11.4
FileOutputStream: 11.9.2, 11.9.3
FileReader: 11.2.2, 11.9.1, 11.9.3
FileWriter: 11.2.1, 11.9.1, 11.9.3
file chooser: 11.9.4
fillArc (of Graphics): 4.3
fillOval (of Graphics): 4.2.2, 4.3
fillRect (of Graphics): 4.3
final: 8.7, 9.12.3
final variable, method, class: 8.7, 9.12.3
float: 3.13.1
floating-point notation: 3.3, 3.13.1
floor (of Math): 3.13.2
flowchart: 6.2.1
763

flow layout: 10.3


FlowLayout: 10.3, 10.4
flush (on System.out):
folder: 1.1
Font: 10.3, 10.11, 10.11.1, 10.15.6
for (for-loop): 7.6
formal parameter (see also, parameter): 5.3, 5.3.1
format (of DecimalFormat): 3.8.1
fractional numbers: 3.3 (see also: double)
frame: 4.2.1, 10.3
framework: 1.7.4, 9.5.2
function: 5.5
\noindent
get (of GregorianCalendar): 4.3.1, 4.8.2
getActionCommand (of ActionEvent): 10.3, 10.15.6
getCaretPosition (of JTextComponent): 10.3, 10.11, 10.15.6
setChanged (of Observable): 10.3, 10.12, 10.15.6
getClickCount (of MouseEvent): 10.15.13
getColumnClass (of TableModel): 10.15.2
getColumnCount (of TableModel): 10.15.2
getColumnName (of TableModel): 10.15.2
getContentPane (of JFrame and JApplet): 10.3, 10.4, 10.15.1, 10.15.6
getFirstIndex (of ListSelectionEvent): 10.15.6
getLastIndex (of ListSelectionEvent): 10.15.6
getGraphics (of JFrame): 4.8.1
getLocationOnScreen (of Component: 10.3, 10.15.6
getMessage (of RuntimeException and Throwable): 10.9, 11.6
getParameter (of JApplet): 10.3, 10.15.1, 10.15.6
getPath (of File): 11.9.3
getRowCount (of TableModel): 10.15.2
getSelectedIndex (of JList): 10.3,10.8, 10.15.6
getSelectedIndices (of JList): 10.3,10.8, 10.15.6
getSelectedFile (of JFileChooser): 11.9.4
getSelectedText (of JTextComponent): 10.3, 10.11, 10.15.6
getSource (of ActionEvent): 10.3, 10.15.6
getText (of AbstractButton): 10.3, 10.15.6
getText (of JLabel): 10.3, 10.15.6
getText (of JTextComponent): 10.3, 10.9, 10.11, 10.15.6
getTime (of GregorianCalendar): 2.4, 4.8.2
getValueAt (of TableModel): 10.15.2
getWindow (of WindowEvent): 10.15.6
getX (of MouseEvent): 10.15.13
764

getY (of MouseEvent): 10.15.13


glass pane: 10.4
graphical user interface: Chapter 10
Graphics: 4.2.2
graphics pen: 4.2.2, 4.4.2
graphics window: 4.2
GregorianCalendar: 2.4, 4.8.2
grid layout: 10.3, 10.7
GridLayout: 10.3, 10.7
GUI: Chapter 10
\noindent
hasMoreTokens (of StringTokenizer): 11.1.1
hash code: 9.7
header line of a method: 5.1, 5.9.5
header line of a program: 2.3
hidden field:
HTML (hyper-text metalanguage): 4.8.4
HumanPlayer model: 9.5
\noindent
icon: 1.4
identifier: 3.2, 3.10
if-statement: see conditional statement
immutable object: 11.1
implements: 9.2, 9.4, 10.4
import: 2.4, 2.5, 9.8
indefinite iteration: 7.2, 7.5
indexing array elements: 8.1, 8.4, 8.8, 8.9
indexOf (of String): 3.5, 10.11.1
induction, proof by: 7.25.2
inductive hypothesis: 7.25.2
inheritance: 1.7.4, 4.2.2, 9.3, 9.12.1
init (of JApplet): 4.8.4, 7.25.6, 10.3, 10.15.1, 10.15.6
initialization (declaration plus assignment): 3.2, 3.13.3
initialization, default, of field variable: 4.4
input to a program: 1.3, 3.8, 4.1
input processing with a loop: 7.5
input via program arguments: 3.8
input file: 11.2
input-view object: 4.1
input-output device: 1.1
InputStream: 11.9.3
InputStreamReader: 11.3, 11.9.3
765

insert (of JTextArea): 10.3, 10.11, 10.15.6


insertion sort: 8.14.1, 8.14.4
instanceof: 9.4, 9.5.1, 9.7
int: 3.2, 3.2.1
Integer: 3.8.1, 9.7
IntegerKey model: 9.2.1
intValue (of Integer): 3.8.1
integers: 3.1
integrated development environment (IDE): 2.2.1, 3.8
integration testing: 6.7, 6.8.8, 6.8.9
interactive input: 4.1
interface: 9.2, 9.9, 9.9.1, 9.9.2
InterrupedException: 2.1, 7.9, 11.6
InterruptedIOException: 11.6
invariant, representation (or class): 4.4, 6.8.3, 6.9.2, 6.9.3
invariant property of loop: 7.3, 7.25.2
IOException: 11.2.1, 11.2.2, 11.6
isCellEditable (of TableModel): 10.15.2
isDirectory (of File): 11.9.3
isEditable (of JTextComponent): 10.3, 10.11, 10.15.6
isEnabled (of AbstractButton): 10.3, 10.15.6
isFile (of File): 11.9.3
isSelected (of AbstractButton): 10.3, 10.15.6
isShowing (of Component: 10.3, 10.15.6
ITALIC (of Font): 10.3, 10.11, 10.15.6
iteration: 7.2
\noindent
Java Development Kit (JDK): 2.2.2, 3.8
javadoc: 5.9.2, 9.8.1
java.awt: 4.2.2, 10.4
java.awt.event: 10.5
java.io: 11.2, 11.2.1, 11.2.2
java.lang: 2.5
java.util: 2.4, 2.5
javax.swing: 4.2.2, 10.4
JApplet: 4.8.4, 10.3, 10.15.1, 10.15.6
JButton: 10.3, 10.4, 10.15.6
JComponent: 10.3, 10.15.6
JFileChooser: 11.9.4
JFrame: 4.2.1, 4.8.1, 10.3, 10.4
JLabel: 10.3, 10.4, 10.15.6
JList: 10.3,10.8, 10.15.6
766

JMenu: 10.3, 10.11, 10.11.1


JMenuBar: 10.3, 10.11, 10.11.1, 10.15.6
JMenuItem: 10.3, 10.11, 10.11.1, 10.15.6
JOptionPane: 4.1, 4.1.1, 10.3, 10.10, 10.15.6
JPanel: 4.2.1, 4.2.2, 10.3, 10.6, 10.15.6
JScrollPane: 10.3,10.8, 10.11, 10.15.6
JTable: 10.15.2
JTextArea: 10.3, 10.11, 10.11.1, 10.15.6
JTextComponent: 10.3, 10.15.6
JTextField: 10.3, 10.9, 10.15.6
\noindent
Key interface: 9.2.1
keyword: 2.3, 3.10
\noindent
label (graphical): 10.3, 10.4
layout: 10.3
layout manager: 10.3
LEFT (of FlowLayout): 10.3
length (of String): 3.5, 7.5.1, 11.1
length (of array): 8.4
Life, game of: 8.13
linear search: 8.14.2
list (graphical): 10.3,10.8 (see also: scrolling list)
list selection event:10.8
ListSelectionEvent: 10.3,10.8, 10.15.6
ListSelectionListener interface: 10.3
literal: 3.1
local variable: 4.4.2
local variable, scope of: 4.4.2
long: 3.13.1
loop: 7.2
loop, nested: 7.7
loop control variable: 7.3, 7.6
loop counter: 7.3, 7.6
Lottery application (uses model object):
\noindent
machine language: 1.1
main method: 2.1
MakeChange application: 3.2.1
MakeChangeAgain application: 6.2.1, 6.3
makeVisible (of List) 12.6
mantissa: 3.3
767

Math (class): 3.13.2


matrix: see array, two-dimensional
max (of Math): 3.12.3
menu (graphical): 10.3, 10.11
menu bar (graphical): 10.3, 10.11
merge sort: 8.14.4
message to an object: 1.3, 5.1
message, syntax of: 2.9.1, 5.9.4
method: 1.3, 5.1
method, accessor: 6.8.3
method, formal syntax and semantics: 5.12.5
method, mutator: 6.8.3
method, overloaded: 6.8.5, 6.8.7, 9.12.4, 9.12.5
method, overridden: 9.12.1, 9.12.2
method, private: 5.6
method, public: 5.1, 5.2, 5.9.6
method, semantics of overloading: 9.12.5
method, semantics of overriding: 9.12.2
method, semantics of recursively defined: 7.20.1
method, static: 5.9.3
min (of Math): 3.12.3
mode of a dialog:
model: 1.5, 6.7, 6.8, 6.8.3, 10.1
Model-View-Controller (MVC) architecture: 1.5, 1.5.1, 6.7, 6.8, 10.1, 10.8, 10.11,
10.12.1
MortgagePaymentCalculator model: 9.2
mouse click: 10.15.3
mouse drag (press-and-release): 10.15.3
mouse event: 10.15.3
MouseAdapter: 10.15.13
mouseClicked (of MouseListener): 10.15.13
mouseEntered (of MouseListener): 10.15.13
mouseExited (of MouseListener): 10.15.13
MouseListener interface: 10.15.13
MouseMotionListener interface: 10.15.13
MouseEvent: 10.15.13
mousePressed (of MouseListener): 10.15.13
mouseReleased (of MouseListener): 10.15.13
moveCaretPosition (of JTextComponent): 10.3, 10.11.1, 10.15.6
MovingBall view: 7.9
MULTIPLE INTERVAL SELECTION (of ListSelectionModel):10.8
MyWriter output view: 5.4
768

MyWriter2 output view: 5.6


\noindent
new: 2.4
nextToken (of StringTokenizer): 11.1.1
NORTH (of BorderLayout): 10.3, 10.6
NoSuchElementException: 11.6
notifyObservers (of Observable): 10.3, 10.12, 10.12.1, 10.15.6
NO OPTION (of JOptionPane): 10.10, 10.15.6
null: 4.1, 4.4, 8.4, 8.5, 10.10
NullPointerException: 8.4, 11.6
NumberFormatException: 4.1, 6.5.1, 11.6
\noindent
object: 1.3, 2.1, 2.3.1, 2.4, 5.9.6, 8.1, 8.4, 9.7, 11.1
Object: 9.7
object file: see binary file
object stream: 11.9.3
ObjectInputStream: 11.9.2, 11.9.3
ObjectOutputStream: 11.9.2, 11.9.3
Observable: 10.3, 10.12, 10.12.1, 10.15.6
Observer interface: 10.3, 10.12, 10.12.1
object-oriented design: 1.7.2
object data type (reference type): 3.5, 3.6, 3.13.3, 9.4
OK button of dialog: 4.1
open a file: 11.2
operating system: 1.4
output from a program: 1.3
output file: 11.2
OutputStream: 11.9.3
OutputStreamWriter: 11.9.3
output-view object: 4.1
overflow:
overloaded method: 6.8.5, 6.8.7, 9.12.4 (see also: method)
overridden method: 9.12.1 (see also: method)
\noindent
pack (of Window): 10.3, 10.4, 10.15.6
package: 2.4, 2.5, 9.8
paint (of JApplet): 4.8.4
paintComponent (of JPanel): 4.2.2, 4.3.1, 4.8.1, 10.3, 10.4, 10.15.6
panel (graphical): 4.2.1, 4.2.2, 10.3, 10.6
Panel:
parameter: 5.3
paste (of JTextComponent): 10.3, 10.11, 10.15.6
769

path name of a file: 11.2.1


patterns of connecting controller to model and views:
pattern of definite iteration: 7.3, 7.6
pattern of processing input: 7.5
pattern of searching: 7.5.1
Paycheck application:
Payroll application: 11.4
PayrollReader view: 11.4
permutation counting: 7.11
PI (of Math): 3.13.2, 5.9.3
PieChartWriter output view: 5.8
pixel: 4.3
PLAIN (of Font): 10.3, 10.11, 10.15.6
PlayerBehavior interface: 9.9
Point: 10.3, 10.15.6
PointWriter output view: 5.8
postcondition of a method: 6.12.2
pow (of Math): 3.3, 3.13.2, 5.9.3
precedence of aritmetic operators: 3.5
precondition of a method: 6.12.2
primitive data type: 3.6, 3.13.1, 9.4
print (of System.out): 2.4
print (of PrintWriter): 11.2.1, 11.9.3
PrintBeeWithName application: 5.3
println (of System.out): 2.3, 2.4
println (of PrintWriter): 11.2.1, 11.9.3
printStackTrace (of Throwable): 11.6
PrintWriter: 11.2.1
private: 4.4, 5.6
private method: 5.6
processor: 1.1
program: 1.2
programming: 1.2
programming language: 1.2
protected field: 9.12.1
public: 2.3
public method: 5.1, 5.2
PuzzleButton controller: 10.7
PuzzleController controller: 8.10
PuzzleFrame view: 10.7
PuzzlePiece model: 8.10
PuzzleWriter output view: 8.10
770

\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

RIGHT (of FlowLayout): 10.3


RoomBehavior interface: 9.9
round (of Math): 3.13.2
row of an array: 8.8
Runnable interface: 10.15.4
RuntimeException: 6.5.1, 10.9, 11.6, 11.6.1
\noindent
scope of a variable: 4.4.2
scope of a parameter: 5.9.5
scroll bar:10.8, 10.11
scrolling list:10.8
selecting (a list item):10.8
selection sort: 8.14.1, 8.14.4
semantics: 2.9.2
sending a message: 1.3, 5.1
sequential file: see file, sequential
setBackground (of JFrame): 10.15.6
setCaretPosition (of JTextComponent): 10.3, 10.11, 10.11.1, 10.15.6
setColor (of Graphics): 4.2.2, 4.3
setDialogTitle (of JFileChooser): 11.9.4
setEditable (of JTextComponent): 10.3, 10.11, 10.15.6
setEnabled (of AbstractButton): 10.3, 10.15.6
setFont (of Component): 10.3, 10.11, 10.11.1, 10.15.6
setBackground (of Component: 10.3
setForeground (of Component: 10.3, 10.4, 10.15.6
setJMenuBar (of JFrame and JApplet): 10.3, 10.11, 10.15.6
setLabel (of Button):
setLayout (of Container): 10.3, 10.4, 10.15.6
setLineWrap (of JTextArea): 10.3, 10.11, 10.11.1, 10.15.6
setLocation (of JFrame and Component): 4.8.1, 10.3, 10.15.6
setSelected (of AbstractButton): 10.3, 10.15.6
setSelectedIndex (of JList): 10.3, 10.15.6
setSelectedIndices (of JList): 10.3, 10.15.6
setSelectionMode (of JList): 10.3,10.8, 10.15.6
setSelectionEnd (of JTextComponent): 10.3, 10.11, 10.15.6
setSelectionStart (of JTextComponent): 10.3, 10.11, 10.15.6
setSize (of JFrame and Component): 4.2.1., 4.8.1, 10.3, 10.4, 10.15.6
setText (of AbstractButton): 10.3, 10.15.6
setText (of JLabel): 10.3, 10.15.6
setText (of JTextComponent): 10.3, 10.11, 10.15.6
setTitle (of JFrame): 4.2.2, 4.8.1, 10.3, 10.4, 10.15.6
setValueAt (of JTable): 10.15.2
772

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

TreasureProperty interface: 9.9.1


Treasury interface: 9.9.1
tree, data-type checking: 3.12.3
trim (of String): 3.6, 6.8.7
try...catch: see exception handler
\noindent
Unicode: 11.2
update (of Observer): 10.3, 10.12, 10.12.1
use case: 6.7, 6.8.1, 10.15.5
\noindent
valueChanged (of ListSelectionListener): 10.3
variable: 3.2
VaultRoom class: 9.9.1
view object: 1.5, 4.1, 10.1
VoteButton controller: 10.15.2
VoteCount application: 8.2
VoteFrame view: 10.15.2
VoteModel model: 10.15.2
VoteModelWithTotals model: 10.15.2
VoteSpreadsheet model: 10.15.2
\noindent
WEST (of BorderLayout): 10.3, 10.6
while-statement: 7.2
whole numbers:
window (graphical): 10.3
windowActivated (of WindowListener): 10.3
windowClosed (of WindowListener): 10.3
windowClosing (of WindowListener): 10.3, 10.5
windowDeactivated (of WindowListener): 10.3
windowDeiconified (of WindowListener): 10.3
windowIconified (of WindowListener): 10.3
windowOpened (of WindowListener): 10.3
Window: 10.3, 10.15.6
WindowAdapter: 10.3, 10.5
WindowEvent: 10.3, 10.5, 10.15.6
WindowListener interface: 10.3, 10.5, 10.6
World-Wide-Web: 4.8.4
wrapper: 9.7
write (of FileWriter): 11.9.1
write (of InputStreamReader): 11.9.3
write (of FileOutputStream): 11.9.3
write (of OutputStreamWriter): 11.9.3
775

write to a file: 11.2


writeBoolean (of ObjectOutputStream): 11.9.3
writeDouble (of ObjectOutputStream): 11.9.2, 11.9.3
writeInt (of ObjectOutputStream): 11.9.2, 11.9.3
writeObject (of ObjectOutputStream): 11.9.2, 11.9.3
Writer: 11.9.3
\noindent
YES OPTION (of JOptionPane): 10.10, 10.15.6

Anda mungkin juga menyukai